diff --git a/AGENTS.md b/AGENTS.md index 462a25d..d9ac08c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,10 @@ follow the hierarchy rather than copying local rules into this file. ## Development and Checks +This is the canonical contributor checklist linked from both READMEs. Keep new +required checks here and in the owning CI workflow rather than maintaining a +shorter, divergent checklist in user onboarding docs. + Run from the repository root using the intended Python environment. Installing changes that environment and may download dependencies: diff --git a/README.md b/README.md index 2c9b1b7..c687684 100644 --- a/README.md +++ b/README.md @@ -4,89 +4,54 @@ A command-line tool for analyzing PyTorch memory snapshots. Set a snapshot database, run built-in queries, and inspect memory usage, leaks, and timelines. +## Start Here + +| Your input or goal | Next step | +| --- | --- | +| Existing `.db` or complete dataset | [Inspect → query → report](docs/en/quickstart.md#step-2-inspect-the-target-and-choose-a-device); no import required | +| Trusted pickle | [Default single-DB import](docs/en/quickstart.md#step-1-import-only-if-needed) | +| No snapshot yet | [Collection prerequisites and entry points](docs/en/quickstart.md#need-to-collect-a-snapshot) | +| Ask an agent to analyze | [Install → restart → verify → ask](docs/en/skills.md#first-agent-session) | + ## Installation +Requires **Python 3.10+**. Offline import/analysis needs no PyTorch or accelerator; +collection uses the workload's compatible GPU/NPU environment. + ```bash -pip install pt-snap-cli +python -m pip install pt-snap-cli ``` For a source checkout and contributor setup, see [Development](#development). ## Quick Start -To start from a raw PyTorch memory snapshot, import the pickle into a SnapshotDB with pt-snap's built-in snapshot support, then list the available query templates: - -> **Security warning:** Import only pickle snapshots from a trusted source. Pickle -> deserialization can execute arbitrary code. The loader rejects non-`builtins` -> global objects, but `pt-snap import` is not a sandbox. +If you already have a DB, skip import. Otherwise use only a **trusted** pickle: +its deserialization can execute arbitrary code; the `builtins` restriction does +not make `pt-snap import` a sandbox. ```bash pt-snap import snapshot.pkl -pt-snap metadata snapshot.pkl.db -pt-snap query --list -``` - -```bash -# Set the snapshot database and device -pt-snap focus snapshot.pkl.db --device 0 - -# List available queries -pt-snap query --list - -# Run a query (automatically uses the focused device) -pt-snap query --template-use memory_peak - -# Detect potential memory leaks -pt-snap query --template-use leak_detection --params '{"min_size": 1024}' +pt-snap overview snapshot.pkl.db --json +# Replace 0 with a device returned by overview. +pt-snap query snapshot.pkl.db --device 0 --template-use memory_peak --json +pt-snap report peak-memory snapshot.pkl.db --device 0 --metric active --limit 20 ``` -To divide a large snapshot into independently replayable files before import, -use exactly one split strategy and an output directory that does not exist: +Default import writes `snapshot.pkl.db` beside the pickle and updates project +`.pt-snap/focus.json` in the current directory. Add `--no-focus` to avoid that +focus change, not to prevent DB replacement: a standalone cache miss/`--force` +can replace an existing same-named DB; choose another `--output-dir` if needed. +The explicit-path inspection/query/report commands do not persist +focus or modify source data. The [walkthrough](docs/en/quickstart.md) explains +byte units, allocated/active/reserved counters, expected output and partial reports. -```bash -pt-snap split snapshot.pkl --slices 4 --output snapshot-slices -``` +### Other Input Formats -See [Splitting Snapshots](docs/en/splitting.md) for device selection, JSON output, -deterministic names, replay validation, and failure-safe publication. - -See the [full quick start guide](docs/en/quickstart.md) for a walkthrough. - -Complete compatibility-v1 sharded artifacts can be focused by directory or -`manifest.json`, without the archived pickle. See [dataset focus and addressing](docs/en/sharded-snapshotdb.md#complete-dataset-focus-and-addressing-p1) -for read-only overview, event routing, `--slice`, and explicit cross-slice limits. - -Opt into a **native, not msinsight-compatible** dataset with -`pt-snap import snapshot.pkl --events-per-slice 50000 --output-dir captures --json`. -The output is `captures/snapshot.pkl.pt-snap-native-v2/`; default standalone import -is unchanged. Reuse validates all shards and content/options/semantic versions. -Nonmatching recognized targets require `--force`; unknown targets are preserved. -Publication/focus failures are compensated, with explicit recovery evidence if -rollback fails (force is not a single crash-atomic swap). See the -[native import guide](docs/en/quickstart.md#optional-import-a-native-sharded-dataset). -Dataset `event` addressing and point-event `active_blocks_at_event` / -`active_memory_callstack_at_event` share batched cross-shard alloc/free sources, -including original artifacts without extensions. Built-in global peaks, event pages, -canonical stack statistics, deduplicated lifecycles, terminal leak candidates and -`report peak-memory` share bounded core semantics; custom SQL/overrides remain unsupported -on datasets. See the [global support matrix](docs/en/querying.md#dataset-global-built-in-support-p3) -and [dataset attribution and coverage](docs/en/querying.md#dataset-point-event-attribution). -Lifecycle queries exceeding SQLite's attachment limit use a private, page-limited -temporary derived database; source shards stay read-only. See the global support -matrix for storage limits, cleanup and memory caveats. - -Explicit `pt-snap import snapshot.pkl --format msinsight --json` produces -`snapshot.pkl.msinsight/` for the fixed msinsight revision's **same original -pickle + adjacent cache** entrance. Inline stacks cost more space than native -interning. Existing compatible targets are never replaced, even with `--force`; -only fully validated identical pt-snap caches are reused. GUI acceptance remains -**pending/not run**; hash equality alone is not acceptance. See -[compatible export and limits](docs/en/sharded-snapshotdb.md#explicit-msinsight-compatible-export-p2). - -See [offline interoperability acceptance and bounded performance](docs/en/interop-acceptance.md) -for the two fixed-version chains, deterministic reference tools, honest three-mode -measurements and the explicitly **pending/not-run GUI checklist**. Diagnostic skills -accept explicitly selected complete validated datasets, not arbitrary directories. +- [Native sharded import](docs/en/quickstart.md#optional-import-a-native-sharded-dataset): opt in with `--events-per-slice`; not msinsight-compatible. +- [Explicit msinsight export](docs/en/quickstart.md#optional-export-an-msinsight-compatible-dataset): separate compatibility and no-replace rules, even with `--force`. +- [Replayable pickle splitting](docs/en/splitting.md): one split strategy and a new output directory; separate from dataset import. +- [Complete-dataset addressing](docs/en/sharded-snapshotdb.md#complete-dataset-focus-and-addressing-p1), [global built-in support](docs/en/querying.md#dataset-global-built-in-support-p3), and [recorded interoperability acceptance](docs/en/interop-acceptance.md): advanced contracts and evidence, not prerequisites for a first report. ## Commands @@ -155,12 +120,15 @@ See the [documentation index](docs/README.md) for all English and Chinese guides ## Development +Use the repository's single [Development and Checks checklist](AGENTS.md#development-and-checks): +fixture provenance, pytest, Ruff, Black, **basedpyright**, and `git diff --check`. +It also explains environment isolation, browser tests and coverage gates. + ```bash -pip install -e ".[dev]" # Install development dependencies -pytest # Run all tests -black --check . && ruff check . # Check formatting and lint +python -m pip install -e ".[dev]" ``` + For the regular suite use `pytest -m 'not slow'`. Default-capacity scale and non-editable installed exporter tests are described in the [dataset acceptance guide](docs/en/dataset-acceptance.md). diff --git a/README_zh.md b/README_zh.md index b7b65c3..218facc 100644 --- a/README_zh.md +++ b/README_zh.md @@ -4,76 +4,51 @@ 用于分析 PyTorch 内存快照的命令行工具。设置快照数据库,运行内置查询,检查内存使用、泄漏和时间线。 +## 从这里开始 + +| 你的输入或目标 | 下一步 | +| --- | --- | +| 已有 `.db` 或完整数据集 | [检查 → 查询 → 报告](docs/zh/quickstart.md#第二步检查目标并选择设备),无需导入 | +| 可信 pickle | [默认单库导入](docs/zh/quickstart.md#第一步仅在需要时导入) | +| 尚无快照 | [采集前提与入口](docs/zh/quickstart.md#尚需采集快照) | +| 让 Agent 帮忙分析 | [安装 → 重启 → 验证 → 提问](docs/zh/skills.md#首次-agent-会话) | + ## 安装 +需要 **Python 3.10+**。离线导入/分析无需 PyTorch 或加速器;采集则在工作负载对应的 +兼容 GPU/NPU 环境中进行。 + ```bash -pip install pt-snap-cli +python -m pip install pt-snap-cli ``` 源码 checkout 与贡献者安装见[开发](#开发)。 ## 快速开始 -> **安全警告:** 只导入来自可信来源的 pickle 快照。反序列化 pickle 可能执行任意代码; -> 实现会拒绝非 builtins 全局对象,但 `pt-snap import` 不是沙箱。 +已有 DB 就跳过导入;否则只使用**可信** pickle。反序列化可能执行任意代码, +`builtins` 限制不代表 `pt-snap import` 是沙箱。 ```bash -# 从原始 pickle 导入;相同内容和配置会自动复用已有 DB pt-snap import snapshot.pkl -pt-snap metadata snapshot.pkl.db - -# 设置快照数据库和设备 -pt-snap focus snapshot.pkl.db --device 0 - -# 列出可用查询 -pt-snap query --list - -# 运行查询(自动使用 focus 中设置的设备) -pt-snap query --template-use memory_peak - -# 检测潜在内存泄漏 -pt-snap query --template-use leak_detection --params '{"min_size": 1024}' +pt-snap overview snapshot.pkl.db --json +# 将 0 替换为 overview 实际返回的设备。 +pt-snap query snapshot.pkl.db --device 0 --template-use memory_peak --json +pt-snap report peak-memory snapshot.pkl.db --device 0 --metric active --limit 20 ``` -如需先把大型快照拆成可独立回放的文件,请只选择一种拆分策略,并指定一个尚不存在的 -输出目录: +默认导入在 pickle 旁写出 `snapshot.pkl.db`,并更新当前目录的项目 +`.pt-snap/focus.json`;加 `--no-focus` 只避免 focus 变化,不防止 DB 替换:单库缓存 +未命中或 `--force` 可能替换同名 DB,必要时选择其他 `--output-dir`。显式路径的检查/查询/报告 +不持久化 focus,也不修改源数据。[完整示例](docs/zh/quickstart.md)解释字节单位、 +allocated/active/reserved 指标、预期输出和报告不完整的情况。 -```bash -pt-snap split snapshot.pkl --slices 4 --output snapshot-slices -``` +### 其他输入格式 -设备选择、JSON 输出、确定性命名、回放验证和失败安全发布见 -[拆分快照](docs/zh/splitting.md)。 - -完整的入门指南见 [Quick Start](docs/zh/quickstart.md)。 - -完整兼容 v1 分片产物可以目录或 `manifest.json` 为 focus,无需已归档 pickle。 -只读 overview、事件路由、`--slice` 和跨片限制见 -[完整数据集 focus 与寻址](docs/zh/sharded-snapshotdb.md#完整数据集-focus-与寻址p1)。 - -用 `pt-snap import snapshot.pkl --events-per-slice 50000 --output-dir captures --json` -显式生成**原生、非 msinsight 兼容**数据集 `captures/snapshot.pkl.pt-snap-native-v2/`。 -默认单库导入不变;复用校验全部片及内容/选项/语义版本。不匹配的已识别目标须 -`--force`,未知目标保留。发布/focus 失败会补偿,回滚自身失败则保留并报告恢复证据 -(force 不是单次崩溃原子 swap)。详见 -[原生分库导入](docs/zh/quickstart.md#可选导入原生分库数据集)。数据集 `event` 寻址及定点 -`active_blocks_at_event` / `active_memory_callstack_at_event` 共用跨片 alloc/free 批量来源, -原版无扩展产物也适用。内建全局峰值、事件分页、canonical 栈统计、去重生命周期、 -终片泄漏候选及 `report peak-memory` 共用有界 core 语义;数据集仍不支持自定义 SQL/override。 -详见[全局支持矩阵](docs/zh/querying.md#数据集全局内建支持p3)及 -[数据集定点归因与覆盖](docs/zh/querying.md#数据集定点事件归因)。 -超过 SQLite 附加上限的生命周期查询使用私有、限制主库页数空间的临时派生数据库,源分片 -保持只读;存储限额、清理及内存边界见全局支持矩阵。 - -显式 `pt-snap import snapshot.pkl --format msinsight --json` 生成 -`snapshot.pkl.msinsight/`,对齐固定 msinsight 版本的**同一原始 pickle + 邻接缓存**入口。 -内联栈比原生去重占更多空间;即使 `--force` 也不替换已有兼容目标,只复用完整校验且 -身份相同的 pt-snap 缓存。GUI **待验收/未运行**,hash 相等不代表验收通过。详见 -[兼容导出及限制](docs/zh/sharded-snapshotdb.md#显式-msinsight-兼容导出p2)。 - -[离线双向验收与有界性能](docs/zh/interop-acceptance.md) 提供固定版本两条链路、 -确定性独立参考工具、真实三模式测量及明确**待验收/not-run 的 GUI 清单**。 -诊断 skills 接受显式选择的完整已校验数据集,不接受任意目录。 +- [原生分库导入](docs/zh/quickstart.md#可选导入原生分库数据集):用 `--events-per-slice` 显式启用,不是 msinsight 兼容格式。 +- [显式 msinsight 导出](docs/zh/quickstart.md#可选导出-msinsight-兼容数据集):独立的兼容合同与 no-replace 规则,`--force` 也不替换。 +- [可回放 pickle 拆分](docs/zh/splitting.md):只选一种拆分策略并使用新目录,与数据集导入不同。 +- [完整数据集寻址](docs/zh/sharded-snapshotdb.md#完整数据集-focus-与寻址p1)、[全局内建支持](docs/zh/querying.md#数据集全局内建支持p3)和[已记录的互操作验收](docs/zh/interop-acceptance.md):按需查看高级合同与证据,不是第一份报告的前置条件。 ## 命令 @@ -138,12 +113,15 @@ helper 会先路由采集,无需先安装 CLI。分析仍需要 CLI 和 Snapsh ## 开发 +统一使用根 [Development and Checks 检查清单](AGENTS.md#development-and-checks): +fixture provenance、pytest、Ruff、Black、**basedpyright** 和 `git diff --check`, +并在同一入口查看环境隔离、浏览器测试与覆盖率门槛。 + ```bash -pip install -e ".[dev]" # 安装开发依赖 -pytest # 运行所有测试 -black --check . && ruff check . # 检查格式和 lint +python -m pip install -e ".[dev]" ``` + 常规套件可用 `pytest -m 'not slow'`。默认容量规模及非 editable 安装包导出测试见 [数据集验收指南](docs/zh/dataset-acceptance.md)。 diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 2a2141d..a28548d 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -27,6 +27,8 @@ Commands must be checked against group modules too; not all options live in - Public behavior changes update both language guides and relevant README entry points. Topic additions/moves also update `docs/README.md` navigation. +- First-use walkthroughs route by input, show the default report before advanced + formats, and explain output units and import/focus writes next to the commands. - Installation walkthroughs lead with PyPI; keep editable developer setup separate. Agent integration uses skills plus CLI, and host restart/loading must be distinguished from on-disk installation status. diff --git a/docs/README.md b/docs/README.md index 668f4ee..3c90c9d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,17 @@ # pt-snap-cli Documentation +## Start by Goal / 按目标开始 + +| Goal / 目标 | English | 中文 | +| --- | --- | --- | +| Prepare or collect input / 准备或采集输入 | [Input choices](en/quickstart.md#prepare-your-input) | [输入分流](zh/quickstart.md#准备输入) | +| First read-only report / 首份只读报告 | [Inspect, query, report](en/quickstart.md#step-2-inspect-the-target-and-choose-a-device) | [检查、查询、报告](zh/quickstart.md#第二步检查目标并选择设备) | +| First agent session / 首次 Agent 接入 | [Install, restart, verify, ask](en/skills.md#first-agent-session) | [安装、重启、验证、提问](zh/skills.md#首次-agent-会话) | +| Contributor checks / 贡献者检查 | [Canonical checklist](../AGENTS.md#development-and-checks) | [统一完整清单](../AGENTS.md#development-and-checks) | + +Advanced protocols and evidence are indexed below; they are not prerequisites +for the default single-DB workflow. 高级协议与证据见下表,不是默认单库流程的前置阅读。 + ## English | Topic | File | diff --git a/docs/en/AGENTS.md b/docs/en/AGENTS.md index 48f0e2d..4c282f4 100644 --- a/docs/en/AGENTS.md +++ b/docs/en/AGENTS.md @@ -7,7 +7,7 @@ Translation counterpart: [Chinese guides](../zh/AGENTS.md). | Guide | Required behavior coverage | | --- | --- | -| `quickstart.md` | PyPI/source setup, trusted import, focus and first query | +| `quickstart.md` | Input/collection routing, PyPI setup, trusted import/focus effects, first report and metric interpretation | | `focus-management.md` | Explicit/environment/project/global resolution and persistence | | `querying.md` | Template discovery, params, semantics, pagination/completeness and timeout | | `splitting.md` | Strategy/device/format, original event IDs, replay validation and absent-destination publication | diff --git a/docs/en/quickstart.md b/docs/en/quickstart.md index d928522..0e70846 100644 --- a/docs/en/quickstart.md +++ b/docs/en/quickstart.md @@ -2,209 +2,196 @@ [中文](../zh/quickstart.md) | English -Get up and running with `pt-snap-cli` in a few minutes. - -For explicit native/compatible import acceptance, the SAME reviewed-fixture -three-mode baseline and the pending GUI checklist, see -[interoperability acceptance](interop-acceptance.md). Default import remains one -standalone DB; `--events-per-slice` opts into native, `--format msinsight` into -compatibility-v1. Trusted import consent is separate from read-only diagnosis. +Go from an existing input to a readable peak-memory report. The default path is +one SnapshotDB; you do not need to choose a sharded format to start. ## Installation +Use **Python 3.10 or newer** in the environment where you want the CLI: + ```bash -pip install pt-snap-cli +python -m pip install pt-snap-cli +pt-snap --help ``` -This installs the `pt-snap` CLI. Contributors working from a source checkout can -use `pip install -e ".[dev]"`; see [Development](../../README.md#development) -in the repository README. +Offline import and analysis do not require PyTorch, TorchNPU, CUDA, or accelerator +hardware. Collection is different: it runs beside the workload in a compatible +PyTorch/CUDA or TorchNPU/Ascend environment. Contributors should use the separate +[Development and Checks entry](../../AGENTS.md#development-and-checks). + +## Prepare Your Input + +| What you have | Shortest next step | +| --- | --- | +| An existing SnapshotDB `.db` | Skip import; use its path in Step 2. | +| A complete native or compatibility-v1 dataset directory / `manifest.json` | Skip import; use that explicit target in Step 2. Keep every member, not just one ready shard. | +| A trusted `.pkl` / `.pickle` snapshot | Use Step 1, then analyze the generated DB. | +| No snapshot yet | Follow the collection entry below, then make a separate trusted-import decision. | + +### Need to Collect a Snapshot? + +- **CUDA/PyTorch:** collect allocator history in your workload's GPU environment + using the [official PyTorch snapshot guide](https://docs.pytorch.org/docs/stable/torch_cuda_memory.html). + Verify the APIs for your installed PyTorch version, choose a bounded history + window, and dump the snapshot after the relevant workload section. Installing + this offline CLI does not instrument or start your training job. +- **Ascend NPU:** use [pt-snap-ascend-npu-collect](../../skills/pt-snap-ascend-npu-collect/SKILL.md) + for TorchNPU API/environment checks and the framework-specific capture route. + Choose the capture ranks, step window and output location. The skill can guide + collection without the CLI if the host has loaded it; see [Agent Skills](skills.md). +- A CSV, SVG or screenshot is not a SnapshotDB. Obtain the database or the + original trusted pickle; do not rename a visualization to `.db`. + +Collection changes the workload's recording behavior and writes a pickle; it is +not read-only analysis. Review that plan separately. Copying a pickle to an +offline machine does not make it safe to deserialize. ## Your First Analysis -### Optional: Import a PyTorch Snapshot - -If you have a raw `.pkl` memory snapshot, import it first with the built-in backend: +### Step 1: Import Only If Needed -> **Security warning:** Use trusted pickle input only. Deserializing a pickle can -> execute arbitrary code. The loader rejects non-`builtins` global objects, but -> `pt-snap import` is not a sandbox. +> **Security warning:** Import only pickle snapshots you trust. Deserialization +> can execute arbitrary code. The loader rejects non-`builtins` global objects, +> but `pt-snap import` is not a sandbox. Agent setup or collection approval is +> not approval to import a pickle. ```bash pt-snap import snapshot.pkl -pt-snap metadata snapshot.pkl.db -pt-snap query --list ``` -The import command stores a SHA-256 source fingerprint and import compatibility metadata inside the -generated database. Repeating the same import with the same device selection reuses the existing DB. -Use `--force` when you intentionally need to rebuild it: +By default this creates `snapshot.pkl.db` **beside the source pickle** and +updates `.pt-snap/focus.json` in the current working directory (project focus). +`--output-dir captures` changes the output parent, not the filename. To create +or reuse the DB **without changing focus**, use: ```bash -pt-snap import snapshot.pkl --force +pt-snap import snapshot.pkl --no-focus ``` -Import currently loads the entire pickle before processing it, so peak memory can be -substantially larger than the input file depending on its object graph and frame count. -Run large imports with sufficient memory headroom. `--device` limits subsequent replay -and database writes, but does not reduce the initial pickle-loading memory peak. +**`--no-focus` is not no-replace:** a standalone cache miss or `--force` can +replace an existing same-named `.db`. Confirm that output is disposable, or use +a different `--output-dir` before importing. -During import, pt-snap replays the selected device's allocator history instead of -copying raw events directly. The resulting SnapshotDB records event-by-event -`allocated`, `active`, and `reserved` totals plus block lifecycles, which makes it -the supported input for `pt-snap query` and `pt-snap report`. This replay behavior -is part of the command workflow; snapshot runtime Python modules are not a public API. +The remaining examples pass an explicit path and device and work with either +choice. Replace `snapshot.pkl.db` with your actual DB or complete dataset path. +Import is a write operation; `overview`, built-in queries, and reports do not +change source data or persist focus. Dataset queries may use private temporary +scratch storage within their [work limits](querying.md#public-dataset-limits). -### Optional: Import a Native Sharded Dataset +### Step 2: Inspect the Target and Choose a Device ```bash -pt-snap import snapshot.pkl --events-per-slice 50000 --output-dir captures --json -pt-snap overview captures/snapshot.pkl.pt-snap-native-v2 --json -pt-snap query --template-use event --slice 0 --json +pt-snap overview snapshot.pkl.db --json ``` -Without `--events-per-slice`, import still produces the existing single DB. With -it, the output is `/.pt-snap-native-v2/`, or the -same directory beside the source if `--output-dir` is omitted. `--format -pt-snap-native-v2` is optional in this mode; `--format single-db` forbids a capacity. -Native output is **not** an msinsight-compatible export. Explicit `--format -msinsight` (alias `compatibility-v1`) selects the separate compatible mode below. - -Capacity counts real chronological positions **per device**, not `max(id)+1` or -negative synthetic boundaries. Original sparse/nonzero IDs, OOM and workspace -events remain native. `--device` selects one event-bearing device; otherwise all -such devices are imported. Empty/static-only and unselected device positions are -reported in `omitted_devices`; selecting an empty device or an all-empty input -fails. Sharding does not bound pickle loading, registry/block rows or peak RSS. - -Reuse requires a validated complete manifest and **every** closed member, with -matching source SHA256, device selection, capacity, native format, manifest, -semantic import, extension and metadata/layout contracts. CLI release version -changes alone do not rebuild. Native identity is separate from future compatible -format identities and from msinsight's salted `cacheHash`. JSON adds `dataset_path`, -`devices`, `slice_count`, `format`, `omitted_devices`, `reused` and `rebuilt`; -`db_path` denotes the dataset directory in this mode. - -A matching target is reused. A different/corrupt recognized native artifact is -preserved unless `--force` explicitly requests replacement. Unknown directories, -extra unrelated files, malformed manifests and symlink targets are never adopted -or deleted, even with force: choose a new `--output-dir`. All devices/shards are -staged beside the target, finalized, closed, metadata-attested and validated; the -source is hashed again before publication. No partial shard is a reusable cache. - -New publication uses an exclusive directory rename. Force replacement first -renames the old artifact into an owned recovery directory, then publishes the -new one; **there is a transient path gap, not a single atomic swap**. Requested -project focus is part of compensation, including a focus write that mutates -before raising: ordinary failure restores old artifact and exact old focus -bytes/state (or previous absence). `--no-focus` skips focus entirely. If rollback -itself fails, recovery paths are preserved and explicitly reported. Do not delete -them before inspecting the evidence. Arbitrary I/O failures, concurrent writers -and crashes do not have unconditional atomic restoration guarantees. - -Dataset focus supports bounded `event` addressing and point-event active block/ -callstack attribution with shared cross-shard sources. Dataset-global peak/list/leak -aggregates remain explicit errors, not one-shard approximations. See -[point-event coverage](querying.md#dataset-point-event-attribution) and the -[native manifest contract](sharded-snapshotdb.md#published-native-dataset-p2). -`SnapshotAnalyzer` remains analysis-only, without import/split methods. +Check `db_path` and choose a `device_id` from `devices`. Below we use `0` as an +example; substitute a device that was actually returned. For a dataset also +check `dataset.status`, format and fingerprint. Stop on an invalid target or +`import_metadata.status: "invalid"`; legacy `"unavailable"` with reason +`"metadata_missing"` means unknown import provenance, not known corruption. +Other unknown statuses/reasons need investigation before diagnosis. -### Optional: Export an msinsight-Compatible Dataset +### Step 3: Read the Counters ```bash -pt-snap import snapshot.pkl --format msinsight --json -pt-snap overview snapshot.pkl.msinsight --json -pt-snap query --template-use event --slice 0 --json +pt-snap query snapshot.pkl.db --device 0 --template-use memory_peak --json ``` -This **explicit** mode targets `Ascend/msinsight@101f65b877a267ffd5f66ea3834706057ba243e5` -and its original-pickle import entrance, not direct directory import. Keep the -same original pickle beside `.msinsight/` for GUI discovery; -pt-snap itself can analyze a relocated complete artifact without pickle. Optional -`--events-per-slice N` defaults to `500000`; `--output-dir` selects another parent, -not an independently supported GUI entrance. `--device` selects an event-bearing -device and omissions are reported. Nonzero/sparse real IDs, OOM/unknown actions, -empty/static-only selections fail, without silent remapping. Native mode still -preserves sparse IDs/OOM/workspace. Trusted pickle and full-input memory costs apply. - -Base tables are physical inline-v1 tables; metadata has import format `1`. -Callstack text is copied once **per event**, not once per distinct stack, so disk -and temporary conversion space can grow substantially. Native interning remains -unchanged. No ordered structured-frame capability is invented from formatted text. -Bounded `event` and point-event active block/callstack dataset queries are -available; global aggregate/report selection remains unsupported. - -Compatible publication is **no-replace**: external/original/user caches are never -overwritten or deleted, even under `--force`. A complete, attested, identical -pt-snap cache is reused (also with force); otherwise choose a new `--output-dir`. -Source content, sourceFile, options, all member hashes and contract versions must -match independently of salted `cacheHash`. A GUI-derived cache modification can -invalidate this attestation, without implying producer rerun. All staged members -close/finalize/validate before exclusive publication and requested focus; ordinary -failure compensates exact old focus bytes, with recovery evidence if rollback fails. -Standalone/native force policies remain unchanged. `SnapshotAnalyzer` is analysis-only. - -**GUI acceptance is pending/not run.** Complete/ready, safe paths/device tables, -and derived allocation-cache availability/buildability also matter; matching hash -alone proves neither GUI reuse nor display parity. Details and fixed source evidence: -[compatible protocol](sharded-snapshotdb.md#explicit-msinsight-compatible-export-p2). +An **illustrative** `rows[0]` (not a promised result for your snapshot): + +```json +{ + "peak_allocated": 8388608, + "peak_allocated_event_id": 10, + "peak_active": 10485760, + "peak_active_event_id": 12, + "peak_reserved": 16777216, + "peak_reserved_event_id": 10 +} +``` -### Optional: Split a Snapshot +All three sizes are **bytes**: here 8, 10 and 16 MiB (`1 MiB = 1048576 bytes`). + +| Counter | Meaning in the replayed allocator history | +| --- | --- | +| `allocated` | Bytes in allocated blocks, excluding blocks already marked pending free. This is block size, not requested tensor payload size. | +| `active` | Allocated plus pending-free block bytes that are not yet reusable. | +| `reserved` | Total segment bytes held by the allocator, including inactive capacity; not all process/device memory. | + +Each metric has its own earliest peak event; event IDs are ordering markers, +not timestamps. Do not subtract independent peaks as if they were simultaneous. +Empty real-event ranges return null peaks: do not invent an event. A large +reserved/active gap alone does not prove fragmentation, a leak or OOM causality. -Use `pt-snap split` when you need smaller, independently replayable files. Split -does not read or change focus: +### Step 4: Generate a Readable Report ```bash -pt-snap split snapshot.pkl --max-entries 50000 --output snapshot-slices +pt-snap report peak-memory snapshot.pkl.db --device 0 --metric active --limit 20 ``` -Use exactly one of `--slices` and `--max-entries`. See -[Splitting Snapshots](splitting.md) for all-device behavior, formats, names, and -atomic publication guarantees. +Expect **Peak memory report**, the selected device/metric/event, separate peak +counters, same-event allocator gaps, and **Active memory by callstack**. The +report explains active blocks at the selected peak, not every reserved byte. +Check the attribution completeness label and active-byte coverage before making +claims: group percentages use `included_bytes` after filtering/ranking, not all +active memory. A capped report can be partial; a budget error is not a partial +successful analysis. Add `--json` for automation. See the +[report contract](querying.md#report-command) for limits and denominators. -### Step 1: Set the Snapshot Database and Device - -Point `pt-snap` to your SQLite snapshot database file: +### Optional: Remember This Target ```bash pt-snap focus snapshot.pkl.db --device 0 +pt-snap report peak-memory ``` -This validates the database and saves the path and device ID to `.pt-snap/focus.json` in your current directory, so you don't need to repeat it. +Unlike the explicit-path analysis above, `focus ` writes project focus. +Skip it if you do not want shared project state. The import default may already +have selected the DB; this command also selects your chosen device. See +[focus precedence and session isolation](focus-management.md). -If you only want to set the database (no device yet): +## Advanced Input Options -```bash -pt-snap focus snapshot.pkl.db -``` +These are optional, not prerequisites for the first report. -### Step 2: List Available Queries +### Optional: Import a Native Sharded Dataset -```bash -pt-snap query --list -``` +`--events-per-slice` opts into a native dataset; it does not make pickle loading +streaming or bound peak RSS. For output paths, complete-member validation, +force/recovery behavior and supported built-ins, read the +[native import contract](sharded-snapshotdb.md#published-native-dataset-p2) and +[global support matrix](querying.md#dataset-global-built-in-support-p3). +Native output is not msinsight-compatible. -### Step 3: Run a Query +### Optional: Export an msinsight-Compatible Dataset -```bash -pt-snap query --template-use memory_peak -``` +`--format msinsight` is explicit opt-in. Existing compatible targets are never +replaced, even with `--force`; compatibility, cache validation, GUI entrance and +fixed-version evidence have their own [protocol contract](sharded-snapshotdb.md#explicit-msinsight-compatible-export-p2). +Read [recorded interoperability acceptance](interop-acceptance.md) before claiming +GUI reuse. A matching hash alone is not GUI acceptance. -### Step 4: Try Advanced Queries +### Optional: Split a Snapshot -```bash -# Detect potential memory leaks -pt-snap query --template-use leak_detection --params '{"min_size": 1024}' +`pt-snap split` creates independently replayable files, not a queried dataset. +It also requires trusted pickle input, exactly one split strategy, and an absent +output directory. It does not change focus. See [Splitting Snapshots](splitting.md). -# Query automatically uses the focused device, or you can override it -pt-snap query --template-use block --device 0 --params '{"min_size": 1048576}' -``` +### Reuse, Rebuild and Memory Costs + +Standalone import stores source SHA-256 and compatibility metadata. Identical +content/options can reuse the DB; cache misses (including missing/invalid +metadata) and `--force` rebuild and can replace the same-named standalone DB. +Unlike native/compatible directory policies, this is not an unknown-target +protection gate. Confirm ownership or choose another `--output-dir` first. +Do not force a rebuild merely because analysis failed; inspect the error. Import loads the entire +pickle before replay; `--device` reduces subsequent work, not that initial peak. +Use sufficient memory headroom. Snapshot runtime modules are not a public API. ## What's Next -- [Focus Management](focus-management.md) — Learn how to manage database and device focus across projects and sessions -- [Querying](querying.md) — Query workflows, template discovery, parameters, and output -- [Splitting Snapshots](splitting.md) — Create independently replayable per-device slices -- [Agent Skills](skills.md) — Install bundled agent workflows into the shared agents directory and Claude -- [Database Schema](database.md) — Understand the SnapshotDB format -- [SnapshotAnalyzer API](snapshot-analyzer-api.md) — Query SnapshotDB files from Python +- [Agent Skills](skills.md) — install, restart, verify and ask for a diagnosis +- [Querying](querying.md) — templates, bounded results, stack detail and reports +- [Focus Management](focus-management.md) — optional persisted/session targets +- [Database Schema](database.md) — counters, metadata and sentinel meanings +- [SnapshotAnalyzer API](snapshot-analyzer-api.md) — analysis with owned Python resources diff --git a/docs/en/skills.md b/docs/en/skills.md index dc989fe..570851c 100644 --- a/docs/en/skills.md +++ b/docs/en/skills.md @@ -4,25 +4,64 @@ `pt-snap-cli` ships agent skills for routing (`pt-snap-helper`), setup, Ascend NPU collection, and memory diagnostics. Agents should start with `pt-snap-helper` and prefer `--json` on commands that accept it. The `pt-snap skill` commands copy those skills into the shared Agent Skills directory and into host-specific directories that Claude Code still requires. -## Collection when the CLI is missing - -For Ascend NPU capture, `pt-snap-helper` routes to `pt-snap-ascend-npu-collect` -without requiring `pt-snap-setup` first. The exact collection skill must already -be loaded by the host or load successfully through its supported loader; the -collection skill owns TorchNPU/environment verification. Without the CLI, -catalog/directory status is `unchecked`, reported separately from host-loading -evidence. Readable skill source is not proof of loading. - -If loading fails or is unavailable, the helper pauses collection handoff and -explains how to make the known skill directory discoverable and restart, or use -a supported loader. Acquiring a missing skill is a separate user choice; the -helper does not implicitly install packages or skills. - -Existing SnapshotDB analysis with a missing CLI still routes to setup. For -capture followed by analysis, collect first, then use setup if the CLI is still -missing. Pickle-only analysis requires an independent trusted-import decision; -setup approval does not approve import. Diagnostic handoff starts only after a -SnapshotDB exists, with the normal capabilities/overview preflight. +## First Agent Session + +1. **Install the CLI** in Python 3.10+ if needed: `python -m pip install pt-snap-cli`. + An agent must get the separate setup approval before installing packages. +2. **Install the skills** for the host you use. The normal default writes to the + user-level shared `agents` and Claude directories: + + ```bash + pt-snap skill install + ``` + + This copies files; it is not a read-only probe. For project scope use + `pt-snap skill install --project --target agents`. See destinations below + before choosing custom/native host paths. +3. **Restart the agent host** after changed files. A running session may still + have the old content loaded. +4. **Verify disk status and host loading separately**: + + ```bash + pt-snap --help + pt-snap skill list --json + ``` + + Check the intended location's `installed` status, then use the host's skill + list/loader to confirm it can load `pt-snap-helper` and the chosen diagnostic + skill. CLI status only proves the inspected files, not host loading. +5. **Ask for one analysis**, giving an existing DB/complete dataset and device. + If you only have a pickle or need collection, start at [Prepare Your Input](quickstart.md#prepare-your-input). + +Example requests (replace paths/devices with your own): + +- “Use pt-snap-helper for `snapshot.pkl.db`, device 0. Explain which blocks and + callstacks were live at the active peak; include coverage and unknowns.” +- “Inspect end-of-trace leak **candidates** in `snapshot.pkl.db`, device 0. + Distinguish retention/pending free from a proven leak; do not change focus.” +- “Gather fragmentation evidence for `snapshot.pkl.db`, device 0, event range + 100–700. Compare same-event allocator gaps and segment behavior; do not infer + an OOM root cause from a gap alone.” + +Diagnostics are read-only: no pickle import, package/skill install, persisted +focus or report-file writes. They validate full capabilities and overview before +queries; `overview.import_metadata.status` matters even when `ok` is true. +A loaded helper routes to one skill rather than running every diagnostic. + +## Workflow Layers and Output Recovery + +- **Normal path:** each skill starts with a short route map, then its required + input/preflight and bounded evidence workflow. Reuse a complete, current + capabilities/overview result rather than printing it again. +- **Full contract:** keep required template parameters/semantics, database and + device identity, nested metadata status, completeness and attribution limits. + Fewer calls do not necessarily mean fewer output bytes. +- **Recovery only when needed:** missing focus, custom install locations and CLI + absence have separate branches below/in the skill. For tool-truncated JSON, + use the packaged [complete-output recovery procedure](../../skills/pt-snap-helper/references/output-recovery.md). + It preserves the full object, offers an in-memory minifying retry, and stops + if the host cannot expose complete output. No `head`, grep, per-template + substitution, raw SQL or scratch-file workaround bypasses preflight. ## List skills @@ -157,3 +196,23 @@ blocks allocated before the range but still live at the selected event remain included. `report peak-memory` selects an unfiltered full-trace peak, so it is only used for an explicitly requested, separately labelled full-trace comparison after validating its peak event. Its results do not replace range-scoped evidence. + +## Collection when the CLI is missing + +For Ascend NPU capture, `pt-snap-helper` routes to `pt-snap-ascend-npu-collect` +without requiring `pt-snap-setup` first. The exact collection skill must already +be loaded by the host or load successfully through its supported loader; the +collection skill owns TorchNPU/environment verification. Without the CLI, +catalog/directory status is `unchecked`, reported separately from host-loading +evidence. Readable skill source is not proof of loading. + +If loading fails or is unavailable, the helper pauses collection handoff and +explains how to make the known skill directory discoverable and restart, or use +a supported loader. Acquiring a missing skill is a separate user choice; the +helper does not implicitly install packages or skills. + +Existing SnapshotDB analysis with a missing CLI still routes to setup. For +capture followed by analysis, collect first, then use setup if the CLI is still +missing. Pickle-only analysis requires an independent trusted-import decision; +setup approval does not approve import. Diagnostic handoff starts only after a +SnapshotDB exists, with the normal capabilities/overview preflight. diff --git a/docs/zh/quickstart.md b/docs/zh/quickstart.md index c04e582..7adb359 100644 --- a/docs/zh/quickstart.md +++ b/docs/zh/quickstart.md @@ -2,179 +2,177 @@ [English](../en/quickstart.md) | 中文 -几分钟内即可上手 `pt-snap-cli`。 - -显式原生/兼容导入验收、相同已审 fixture 三模式性能及待验收 GUI 清单见 -[双向互操作验收](interop-acceptance.md)。默认导入仍是单库,`--events-per-slice` -选择原生,`--format msinsight` 选择 compatibility-v1;可信导入同意与只读诊断分开。 +从已有输入得到一份可读的峰值内存报告。默认路径是单个 SnapshotDB,无需先选择分库格式。 ## 安装 +在希望使用 CLI 的环境中准备 **Python 3.10 或更新版本**: + ```bash -pip install pt-snap-cli +python -m pip install pt-snap-cli +pt-snap --help ``` -这会安装 `pt-snap` 命令行工具。从源码 checkout 开发时,可使用 -`pip install -e ".[dev]"`;详见仓库 README 的[开发](../../README_zh.md#开发)一节。 +离线导入和分析不需要 PyTorch、TorchNPU、CUDA 或加速器硬件。采集则不同:它在运行 +工作负载的、兼容的 PyTorch/CUDA 或 TorchNPU/Ascend 环境中进行。贡献者请使用独立的 +[开发与完整检查入口](../../AGENTS.md#development-and-checks)。 -## 第一次分析 +## 准备输入 + +| 你已有的输入 | 最短下一步 | +| --- | --- | +| SnapshotDB `.db` | 跳过导入,在第二步直接使用其路径。 | +| 完整的原生或 compatibility-v1 数据集目录 / `manifest.json` | 跳过导入,在第二步显式指定该目标。保留全部成员,不能只拿一个 ready 分片。 | +| 可信的 `.pkl` / `.pickle` 快照 | 完成第一步,再分析生成的 DB。 | +| 尚无快照 | 先看下面的采集入口,随后单独决定是否进行可信导入。 | + +### 尚需采集快照? + +- **CUDA/PyTorch:** 在工作负载的 GPU 环境中,按 + [PyTorch 官方快照指南](https://docs.pytorch.org/docs/stable/torch_cuda_memory.html) + 记录分配器历史。核实已安装 PyTorch 版本的 API,选择有界记录窗口,在相关工作负载 + 阶段结束后导出快照。安装离线 CLI 不会自动插桩或启动训练任务。 +- **昇腾 NPU:** 使用 [pt-snap-ascend-npu-collect](../../skills/pt-snap-ascend-npu-collect/SKILL.md) + 检查 TorchNPU API/环境并选择框架对应的采集路径。明确采集 rank、step 窗口和输出位置。 + 宿主已加载该 skill 时,无需先有 CLI 即可指导采集;参见 [Agent Skills](skills.md)。 +- CSV、SVG 或截图不是 SnapshotDB。请获取数据库或原始可信 pickle,不要将可视化文件 + 改名为 `.db`。 + +采集会改变工作负载的记录行为并写出 pickle,不是只读分析,应单独审阅采集计划。 +把 pickle 复制到离线机器并不会使反序列化变得安全。 -### 可选:导入 PyTorch 快照 +## 第一次分析 -如果你手上是原始 `.pkl` 内存快照,请先使用内建后端导入: +### 第一步:仅在需要时导入 -> **安全警告:** 只能使用可信的 pickle 输入。反序列化 pickle 可能执行任意代码; -> 实现会拒绝非 builtins 全局对象,但 `pt-snap import` 不是沙箱。 +> **安全警告:** 只能导入你信任的 pickle 快照。反序列化可能执行任意代码;实现会 +> 拒绝非 `builtins` 全局对象,但 `pt-snap import` 不是沙箱。Agent 的 setup 或采集 +> 授权不等于 pickle 导入授权。 ```bash pt-snap import snapshot.pkl -pt-snap metadata snapshot.pkl.db -pt-snap query --list ``` -导入命令会在数据库内保存原始文件的 SHA-256 和导入兼容性 metadata。再次使用相同设备配置 -导入相同内容时,会直接复用已有 DB。如需明确重建,可使用: +默认在**源 pickle 旁**生成 `snapshot.pkl.db`,并更新当前工作目录中的 +`.pt-snap/focus.json`(项目 focus)。`--output-dir captures` 改变输出父目录,不改变 +文件名。若希望创建或复用 DB,**但不修改 focus**: ```bash -pt-snap import snapshot.pkl --force +pt-snap import snapshot.pkl --no-focus ``` -当前导入流程会先完整加载 pickle,再开始处理,因此内存峰值可能明显高于输入文件大小, -具体取决于对象图和 frame 数量。导入大型快照时请预留充足内存。`--device` 只能减少 -后续回放和数据库写入量,无法降低最初加载 pickle 时的内存峰值。 +**`--no-focus` 不等于 no-replace:** 单库缓存未命中或使用 `--force` 时,可能替换 +已有同名 `.db`。请先确认该输出可被替换,或在导入前选择另一个 `--output-dir`。 -导入时,pt-snap 会回放所选设备的分配器历史,而不是直接复制原始事件。生成的 -SnapshotDB 会记录每个事件后的 `allocated`、`active`、`reserved` 总量和 block -生命周期,供 `pt-snap query` 与 `pt-snap report` 使用。回放是命令工作流的一部分; -快照运行时的 Python 模块不是公开 API。 +下面的分析示例均显式传入路径和设备,两种导入选择都可使用。将 `snapshot.pkl.db` +换成实际 DB 或完整数据集路径。导入是写操作;`overview`、内建查询和报告不修改源数据, +也不持久化 focus。数据集查询可能在[工作限额](querying.md#公开数据集预算)内使用私有临时 +scratch 存储。 -### 可选:导入原生分库数据集 +### 第二步:检查目标并选择设备 ```bash -pt-snap import snapshot.pkl --events-per-slice 50000 --output-dir captures --json -pt-snap overview captures/snapshot.pkl.pt-snap-native-v2 --json -pt-snap query --template-use event --slice 0 --json +pt-snap overview snapshot.pkl.db --json ``` -不指定 `--events-per-slice` 时仍生成已有单 DB;指定后生成 -`/<完整输入文件名>.pt-snap-native-v2/`,省略 `--output-dir` 则放在源文件旁。 -此模式可显式指定 `--format pt-snap-native-v2`;`--format single-db` 不允许容量参数。 -原生产物**不是** msinsight 兼容导出;显式 `--format msinsight`(别名 -`compatibility-v1`)选择下述独立兼容模式。 - -容量按**各设备**的真实时间顺序位置计数,不使用 `max(id)+1`,不计负 ID 合成边界。 -保留原始稀疏/非零 ID、OOM 和 workspace 事件。`--device` 选择一个有事件设备,默认 -导入全部有事件设备;空/仅静态及未选设备位置由 `omitted_devices` 披露。选择空设备或 -全部为空的输入会失败。分库不限制 pickle 加载、registry/block 行数或峰值 RSS。 - -缓存复用须校验完整 manifest 与**全部**已关闭成员,并匹配源 SHA256、设备选择、容量、 -原生格式、manifest、导入语义、扩展及 metadata/布局合同版本。仅 CLI 发布版本变化不会 -重建;原生身份与后续兼容格式及 msinsight salted `cacheHash` 分离。JSON 增加 -`dataset_path`、`devices`、`slice_count`、`format`、`omitted_devices`、`reused` 和 -`rebuilt`;此模式下 `db_path` 为数据集目录。 - -匹配目标可复用;不同或成员损坏的已识别原生数据集会保留,除非显式 `--force` 替换。 -未知目录、无关额外文件、非法 manifest 和符号链接目标即使 force 也不会被接管或删除, -请换一个 `--output-dir`。所有设备/分片在目标同级暂存,完成回填、关闭、metadata 写入与 -校验,并在发布前复核源哈希;部分就绪片不构成缓存。 - -新目标使用排他的目录 rename 发布。force 先把旧产物移入所有权受检的恢复目录,再发布 -新产物;**路径有短暂空档,不是单次原子 swap**。请求的项目 focus 纳入补偿,写入已修改 -文件后才抛错也要处理:普通失败恢复旧产物及旧 focus 原始字节/状态(或原先不存在)。 -`--no-focus` 完全跳过 focus。回滚自身失败会保留并明确报告恢复路径,检查证据前不要删除。 -不保证任意 I/O 故障、并发 writer 或崩溃下无条件原子恢复。 - -数据集 focus 支持有界 `event` 寻址及共用跨片来源的定点 active block/调用栈归因。 -数据集全局 peak/list/leak 聚合仍明确失败,不按一片近似。详见 -[定点覆盖](querying.md#数据集定点事件归因)及[原生 manifest 合同](sharded-snapshotdb.md#已发布原生数据集p2)。 -`SnapshotAnalyzer` 仍只负责分析,不新增 import/split 方法。 +检查 `db_path`,从 `devices` 中选择 `device_id`。下文以 `0` 为例,请替换为实际返回的 +设备。数据集还需检查 `dataset.status`、format 和 fingerprint。目标无效或 +`import_metadata.status: "invalid"` 时停止;旧数据的 `"unavailable"` 且 reason 为 +`"metadata_missing"` 表示导入来源未知,不等于已确认损坏。其他未知状态/原因应先调查, +再进行诊断。 -### 可选:导出 msinsight 兼容数据集 +### 第三步:读取计数器 ```bash -pt-snap import snapshot.pkl --format msinsight --json -pt-snap overview snapshot.pkl.msinsight --json -pt-snap query --template-use event --slice 0 --json +pt-snap query snapshot.pkl.db --device 0 --template-use memory_peak --json ``` -此**显式**模式仅对齐 `Ascend/msinsight@101f65b877a267ffd5f66ea3834706057ba243e5` -的原始 pickle 导入入口,不是独立目录入口。GUI 发现要求同一原始 pickle 邻接 -`<完整输入文件名>.msinsight/`;pt-snap 可在没有 pickle 时分析已搬迁完整产物。 -可选 `--events-per-slice N` 默认 `500000`;`--output-dir` 选择其他父目录,不意味着 -GUI 支持独立入口。`--device` 选择有事件设备,并披露省略设备。非零/稀疏真实 ID、 -OOM/未知 action、空/仅静态选择明确失败,不静默映射;原生模式仍保留稀疏 ID、OOM、 -workspace。可信 pickle 与完整加载的内存成本仍适用。 - -基础表是物理内联 v1 表,metadata import format 为 `1`。调用栈文本按**每个事件** -复制,不按不同栈只存一次,磁盘与暂存转换空间可能明显增加;原生去重不变。 -不从格式化文本虚构有序结构化 frames。数据集可用有界 `event` 和定点 active -block/调用栈查询;全局聚合/报告事件选择仍不支持。 - -兼容发布采用 **no-replace**:外部/原版/用户缓存即使 `--force` 也不覆盖、不删除。 -仅完整、已证明且身份相同的 pt-snap 缓存可复用(force 也复用);否则换新 -`--output-dir`。源内容、sourceFile、选项、全部成员哈希和合同版本须独立于 salted -`cacheHash` 匹配。GUI 修改派生缓存可能使该证明失效,但不等于重跑生产者。 -全部暂存成员关闭/最终化/校验后才排他发布并写请求的 focus;普通失败补偿旧 focus -原始字节,回滚失败则保留恢复证据。单库/原生 force 策略不变,`SnapshotAnalyzer` 只分析。 - -**GUI 待验收/未运行。** complete/ready、安全路径/设备表及派生 allocation cache -存在或可构建同样重要;hash 相同不证明 GUI 复用或显示一致。详见 -[兼容协议及固定源码证据](sharded-snapshotdb.md#显式-msinsight-兼容导出p2)。 +以下是**示意**的 `rows[0]`,不是对你的快照输出的承诺: + +```json +{ + "peak_allocated": 8388608, + "peak_allocated_event_id": 10, + "peak_active": 10485760, + "peak_active_event_id": 12, + "peak_reserved": 16777216, + "peak_reserved_event_id": 10 +} +``` -### 可选:拆分快照 +三个大小均以**字节(bytes)**为单位,此例分别为 8、10、16 MiB +(`1 MiB = 1048576 bytes`)。 + +| 计数器 | 分配器回放历史中的含义 | +| --- | --- | +| `allocated` | 已分配 block 的字节数,不含已标为 pending free 的 block;是 block 大小,不是请求的 tensor payload 大小。 | +| `active` | 已分配与 pending-free、尚不可复用的 block 字节数之和。 | +| `reserved` | 分配器持有的 segment 总字节数,包含 inactive 容量;不是整个进程/设备的全部内存。 | + +各指标独立选取最早达到峰值的事件,event ID 表示顺序而非时间戳。不能把不同事件的峰值 +直接相减当作同一时刻的差值。真实事件范围为空时峰值为 null,不得虚构事件。 +reserved/active 差值大,本身不能证明碎片、泄漏或 OOM 原因。 -如需生成更小、可独立回放的文件,可使用 `pt-snap split`。该命令不会读取或修改 focus: +### 第四步:生成可读报告 ```bash -pt-snap split snapshot.pkl --max-entries 50000 --output snapshot-slices +pt-snap report peak-memory snapshot.pkl.db --device 0 --metric active --limit 20 ``` -`--slices` 和 `--max-entries` 必须且只能指定一个。多设备行为、格式、命名和原子发布 -保证见[拆分快照](splitting.md)。 +输出包含 **Peak memory report**、选定的设备/指标/事件、各项独立峰值、同事件分配器 +差值和 **Active memory by callstack**。报告解释所选峰值事件的 active blocks, +不是分解全部 reserved 字节。下结论前检查归因完整性标签和 active 字节覆盖率: +分组百分比以过滤/排名后 `included_bytes` 为分母,并非全部 active 内存。 +有上限的报告可能不完整;预算错误不等于部分分析成功。自动化可加 `--json`。 +限额与分母详见[报告合同](querying.md#report-命令)。 -### 第一步:设置快照数据库和设备 - -将 `pt-snap` 指向你的 SQLite 快照数据库文件: +### 可选:记住此目标 ```bash pt-snap focus snapshot.pkl.db --device 0 +pt-snap report peak-memory ``` -该命令会验证数据库,并将路径和设备 ID 保存到当前目录的 `.pt-snap/focus.json`,之后无需重复指定。 +与上面的显式路径分析不同,`focus ` 会写项目 focus。不想改变共享项目状态就跳过。 +默认导入可能已经选择了 DB;此命令还会选择你指定的设备。参见 +[focus 优先级与会话隔离](focus-management.md)。 -如果只需设置数据库(暂不指定设备): +## 高级输入选项 -```bash -pt-snap focus snapshot.pkl.db -``` +这些都是可选项,不是第一份报告的前置条件。 -### 第二步:列出可用查询 +### 可选:导入原生分库数据集 -```bash -pt-snap query --list -``` +`--events-per-slice` 显式启用原生数据集,但不会让 pickle 加载变为流式,也不限制峰值 +RSS。输出路径、完整成员校验、force/恢复行为及支持的内建查询见 +[原生导入合同](sharded-snapshotdb.md#已发布原生数据集p2)和 +[全局支持矩阵](querying.md#数据集全局内建支持p3)。原生产物不是 msinsight 兼容格式。 -### 第三步:运行查询 +### 可选:导出 msinsight 兼容数据集 -```bash -pt-snap query --template-use memory_peak -``` +`--format msinsight` 是显式选择。已有兼容目标即使 `--force` 也不替换;兼容性、 +缓存校验、GUI 入口及固定版本证据有独立的 +[协议合同](sharded-snapshotdb.md#显式-msinsight-兼容导出p2)。声称 GUI 复用前请阅读 +[已记录的互操作验收](interop-acceptance.md),仅 hash 相同不等于 GUI 验收通过。 + +### 可选:拆分快照 -### 第四步:尝试高级查询 +`pt-snap split` 生成可独立回放的文件,不是待查询数据集。它同样需要可信 pickle, +只选一种拆分策略,并使用尚不存在的输出目录;不会改变 focus。参见[拆分快照](splitting.md)。 -```bash -# 检测潜在内存泄漏 -pt-snap query --template-use leak_detection --params '{"min_size": 1024}' +### 复用、重建与内存成本 -# 查询自动使用 focus 中设置的设备,也可以显式覆盖 -pt-snap query --template-use block --device 0 --params '{"min_size": 1048576}' -``` +单库导入保存源 SHA-256 和兼容性 metadata,相同内容/选项可复用 DB;缓存未命中 +(包括缺失/无效 metadata)和 `--force` 都会重建,并可能替换同名 DB。它与原生/兼容 +目录的策略不同,并没有未知目标保护门禁。请先确认输出所有权或选择其他 `--output-dir`。 +不要仅因分析失败就强制重建,应先检查错误。 +导入会在回放前完整加载 pickle;`--device` 减少后续工作,不降低最初的加载峰值。 +请预留足够内存。snapshot 运行时模块不是公开 API。 ## 下一步 -- [Focus 管理](focus-management.md) — 学习如何在多个项目和会话之间管理数据库和设备焦点 -- [运行查询](querying.md) — 查询流程、模板发现、参数和输出说明 -- [拆分快照](splitting.md) — 创建可独立回放的逐设备切片 -- [Agent Skills](skills.md) — 将随包 agent 工作流安装到共享 agents 目录和 Claude -- [数据库格式](database.md) — 了解 SnapshotDB 格式 -- [SnapshotAnalyzer API](snapshot-analyzer-api.md) — 从 Python 查询 SnapshotDB 文件 +- [Agent Skills](skills.md) — 安装、重启、验证并发起诊断 +- [查询指南](querying.md) — 模板、有界结果、栈详情与报告 +- [Focus 管理](focus-management.md) — 可选的持久化/会话目标 +- [数据库格式](database.md) — 计数器、metadata 与哨兵含义 +- [SnapshotAnalyzer API](snapshot-analyzer-api.md) — 明确资源生命周期的 Python 分析 diff --git a/docs/zh/skills.md b/docs/zh/skills.md index e86f292..1c0f267 100644 --- a/docs/zh/skills.md +++ b/docs/zh/skills.md @@ -4,21 +4,54 @@ `pt-snap-cli` 附带用于路由(`pt-snap-helper`)、环境安装、昇腾 NPU 采集和内存诊断的 agent skill。Agent 应先使用 `pt-snap-helper`,并在支持 `--json` 的命令上优先使用该选项。`pt-snap skill` 命令会把这些 skill 复制到共享的 Agent Skills 目录,以及 Claude Code 仍然需要的独立目录。 -## CLI 缺失时的采集路由 - -对于昇腾 NPU 采集,`pt-snap-helper` 会选择 `pt-snap-ascend-npu-collect`,无需先走 -`pt-snap-setup`。该采集 skill 必须已被宿主加载,或通过宿主支持的加载器成功加载; -TorchNPU 与环境验证由采集 skill 负责。缺少 CLI 时,目录源和安装目录状态记为 -`unchecked`(未检查),与宿主加载证据分别报告。源码可读不能证明已加载。 - -如果加载失败或没有可用的加载方式,helper 会暂停采集交接,说明如何让宿主发现已知的 -skill 目录并重启,或使用支持的加载器。获取缺失的 skill 是独立的用户选择;helper -不会隐式安装 Python 包或 skill。 - -已有 SnapshotDB 的分析任务在缺 CLI 时仍先交给 setup。先采集再分析的任务先完成采集, -之后若 CLI 仍缺失再走 setup。仅有 pickle 的分析任务需要独立的可信导入决定;批准 -setup 不代表批准导入。只有 SnapshotDB 已存在时才交给诊断 skill,并执行正常的 -capabilities/overview 前置检查。 +## 首次 Agent 会话 + +1. **安装 CLI**:如尚未安装,在 Python 3.10+ 环境运行 `python -m pip install pt-snap-cli`。 + Agent 安装软件包前仍须单独取得 setup 授权。 +2. **安装 skill**:选择你使用的宿主。常规默认写用户级共享 `agents` 和 Claude 目录: + + ```bash + pt-snap skill install + ``` + + 这会复制文件,不是只读探测。项目级可用 + `pt-snap skill install --project --target agents`。需要自定义/宿主原生路径时,先看下面的安装位置。 +3. 文件有变化后**重启 Agent 宿主**,运行中的会话可能仍加载旧内容。 +4. **分别验证磁盘状态与宿主加载**: + + ```bash + pt-snap --help + pt-snap skill list --json + ``` + + 检查预期位置为 `installed`,再通过宿主的 skill 列表/加载器确认可以加载 + `pt-snap-helper` 和所选诊断 skill。CLI 状态只证明已检查文件,不证明宿主已加载。 +5. **提出一个分析请求**,给出已有 DB/完整数据集和设备。仅有 pickle 或尚需采集时, + 先看[准备输入](quickstart.md#准备输入)。 + +请求示例(替换为你的路径与设备): + +- “用 pt-snap-helper 分析 `snapshot.pkl.db` 的 device 0,解释 active 峰值时存活的 + blocks 和调用栈,并说明覆盖率与未知项。” +- “检查 `snapshot.pkl.db` 的 device 0 在 trace 末尾的泄漏**候选**,区分保留、 + pending free 与已证实泄漏,不要修改 focus。” +- “为 `snapshot.pkl.db` 的 device 0、事件范围 100–700 收集碎片证据,比较同事件的 + 分配器差值与 segment 行为,不要仅凭差值推断 OOM 根因。” + +诊断保持只读:不导入 pickle、不安装包/skill、不持久化 focus、不写报告文件。 +查询前验证完整 capabilities 和 overview;即使 `ok` 为 true,也要检查 +`overview.import_metadata.status`。已加载的 helper 只路由到一个 skill,不同时跑所有诊断。 + +## 工作流分层与输出恢复 + +- **常规路径:** 每个 skill 先给短路线图,再进入必需输入/预检和有界证据工作流。 + 复用完整且仍有效的 capabilities/overview,不重复输出整个目录。 +- **完整合同:** 保留必需模板参数/语义、数据库与设备身份、嵌套 metadata 状态、 + 完整性和归因边界。调用更少不等于输出字节更少。 +- **仅遇到异常时恢复:** focus 缺失、自定义安装位置、CLI 缺失有独立分支。 + 工具截断 JSON 时,使用随包的[完整输出恢复流程](../../skills/pt-snap-helper/references/output-recovery.md): + 保留完整对象,可在内存中压缩空白后重取;宿主无法提供完整输出时停止。 + 不得用 `head`、grep、逐模板替代、原始 SQL 或 scratch 文件绕过预检。 ## 列出 skill @@ -145,3 +178,19 @@ Skill 管理只通过 CLI 提供。 开始前分配、但在所选事件仍存活的块仍纳入归因。`report peak-memory` 会选择 未过滤的全 trace 峰值,因此仅在用户明确要求全 trace 对照、且峰值事件已通过 校验时使用,并单独标注;其结果不能替代范围内证据。 + +## CLI 缺失时的采集路由 + +对于昇腾 NPU 采集,`pt-snap-helper` 会选择 `pt-snap-ascend-npu-collect`,无需先走 +`pt-snap-setup`。该采集 skill 必须已被宿主加载,或通过宿主支持的加载器成功加载; +TorchNPU 与环境验证由采集 skill 负责。缺少 CLI 时,目录源和安装目录状态记为 +`unchecked`(未检查),与宿主加载证据分别报告。源码可读不能证明已加载。 + +如果加载失败或没有可用的加载方式,helper 会暂停采集交接,说明如何让宿主发现已知的 +skill 目录并重启,或使用支持的加载器。获取缺失的 skill 是独立的用户选择;helper +不会隐式安装 Python 包或 skill。 + +已有 SnapshotDB 的分析任务在缺 CLI 时仍先交给 setup。先采集再分析的任务先完成采集, +之后若 CLI 仍缺失再走 setup。仅有 pickle 的分析任务需要独立的可信导入决定;批准 +setup 不代表批准导入。只有 SnapshotDB 已存在时才交给诊断 skill,并执行正常的 +capabilities/overview 前置检查。 diff --git a/skills/pt-snap-helper/SKILL.md b/skills/pt-snap-helper/SKILL.md index 0fc07a8..ab6484b 100644 --- a/skills/pt-snap-helper/SKILL.md +++ b/skills/pt-snap-helper/SKILL.md @@ -20,6 +20,49 @@ Use this skill when: Do not use this skill to run leak, peak, or fragmentation analysis. Hand off to the matching diagnostic skill after routing. +## Normal route first + +1. Choose one goal/input row below; collection comes before later analysis. +2. Verify the selected skill's availability and actual host loading using the + prerequisite contract. Do not install, import or diagnose from this helper. +3. Hand off the explicit target/device and any complete reusable preflight + results; the diagnostic skill validates them before querying. + +Read only the recovery branch that applies: [custom/host availability](#interpret-availability-in-the-inspected-scope), +[missing focus](#missing-focus-target-with-sibling-candidates), or +[complete-output recovery](references/output-recovery.md). The full +[preflight](#shared-diagnostic-preflight-contract), [boundaries](#boundaries) and +[output](#output) remain mandatory; this route map is not a replacement. + +## Routing matrix + +Choose one next skill. If the goal is still ambiguous after this table, ask +the user; do not start all diagnostic skills. + +Apply the capture row first when capture is the current stage, including a +capture-then-analysis request. The missing-CLI analysis cells apply when the +user is ready for analysis, not to the earlier collection stage. Every handoff +requires host-loading evidence; a next-skill name alone is not a completed handoff. + +| User goal | Existing SnapshotDB | Only pickle | `pt-snap` missing | Need Ascend NPU capture | +| --- | --- | --- | --- | --- | +| Install or verify the CLI | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | +| Collect an Ascend NPU pickle | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | +| Leak / live allocations at end of trace | `pt-snap-memory-leak` | Trusted import first, then leak | `pt-snap-setup` first | Collect first, then trusted import, then leak | +| What was live at a peak event | `pt-snap-memory-peak-breakdown` | Trusted import first, then peak | `pt-snap-setup` first | Collect first, then trusted import, then peak | +| Allocator gaps / reserved-pool pressure | `pt-snap-memory-fragmentation` | Trusted import first, then fragmentation | `pt-snap-setup` first | Collect first, then trusted import, then fragmentation | + +Exact next-skill names: + +- `pt-snap-setup` +- `pt-snap-ascend-npu-collect` +- `pt-snap-memory-leak` +- `pt-snap-memory-peak-breakdown` +- `pt-snap-memory-fragmentation` + +CSV, SVG, and other visualization files are not SnapshotDB input. If the user +only has those, stop and ask for a `.db` or a trusted pickle. + ## Prerequisite: check skill availability If `pt-snap` is on PATH, inspect bundled skill status with JSON: @@ -210,35 +253,6 @@ report gap/coverage; one query/report cumulative 100000 fetched/output rows /64 serialized values is NOT an RSS ceiling. Budget failure is no partial global proof. This helper still only hands off; it does not execute analysis queries. -## Routing matrix - -Choose one next skill. If the goal is still ambiguous after this table, ask -the user; do not start all diagnostic skills. - -Apply the capture row first when capture is the current stage, including a -capture-then-analysis request. The missing-CLI analysis cells apply when the -user is ready for analysis, not to the earlier collection stage. Every handoff -requires host-loading evidence; a next-skill name alone is not a completed handoff. - -| User goal | Existing SnapshotDB | Only pickle | `pt-snap` missing | Need Ascend NPU capture | -| --- | --- | --- | --- | --- | -| Install or verify the CLI | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | -| Collect an Ascend NPU pickle | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | -| Leak / live allocations at end of trace | `pt-snap-memory-leak` | Trusted import first, then leak | `pt-snap-setup` first | Collect first, then trusted import, then leak | -| What was live at a peak event | `pt-snap-memory-peak-breakdown` | Trusted import first, then peak | `pt-snap-setup` first | Collect first, then trusted import, then peak | -| Allocator gaps / reserved-pool pressure | `pt-snap-memory-fragmentation` | Trusted import first, then fragmentation | `pt-snap-setup` first | Collect first, then trusted import, then fragmentation | - -Exact next-skill names: - -- `pt-snap-setup` -- `pt-snap-ascend-npu-collect` -- `pt-snap-memory-leak` -- `pt-snap-memory-peak-breakdown` -- `pt-snap-memory-fragmentation` - -CSV, SVG, and other visualization files are not SnapshotDB input. If the user -only has those, stop and ask for a `.db` or a trusted pickle. - ### Existing SnapshotDB A standalone SnapshotDB is a SQLite database (typically `.db`) with a diff --git a/skills/pt-snap-helper/references/output-recovery.md b/skills/pt-snap-helper/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/skills/pt-snap-helper/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/skills/pt-snap-memory-fragmentation/SKILL.md b/skills/pt-snap-memory-fragmentation/SKILL.md index 488cf5c..e0577a9 100644 --- a/skills/pt-snap-memory-fragmentation/SKILL.md +++ b/skills/pt-snap-memory-fragmentation/SKILL.md @@ -17,6 +17,20 @@ largest contiguous free region. Therefore, `reserved - active` is not pure fragmentation, and no definitive fragmentation ratio or OOM root cause can be derived from this workflow. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [preflight](#prerequisite-phase), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#diagnostic-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs - A SnapshotDB path, supplied explicitly or resolved from the current diff --git a/skills/pt-snap-memory-fragmentation/references/output-recovery.md b/skills/pt-snap-memory-fragmentation/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/skills/pt-snap-memory-fragmentation/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/skills/pt-snap-memory-leak/SKILL.md b/skills/pt-snap-memory-leak/SKILL.md index 0e25502..9cdf92d 100644 --- a/skills/pt-snap-memory-leak/SKILL.md +++ b/skills/pt-snap-memory-leak/SKILL.md @@ -11,6 +11,20 @@ Use this skill to diagnose memory that remains live in a PyTorch SnapshotDB and This is an evidence-gathering workflow. A single snapshot cannot confirm a leak. An allocation without a recorded free event is a candidate that was still live when tracing ended, not proof that it can never be released. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [preflight](#prerequisite-phase), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#diagnostic-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs - A SnapshotDB path, either supplied explicitly or resolved by the current `pt-snap focus`. diff --git a/skills/pt-snap-memory-leak/references/output-recovery.md b/skills/pt-snap-memory-leak/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/skills/pt-snap-memory-leak/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/skills/pt-snap-memory-peak-breakdown/SKILL.md b/skills/pt-snap-memory-peak-breakdown/SKILL.md index 791ec41..b0c12e4 100644 --- a/skills/pt-snap-memory-peak-breakdown/SKILL.md +++ b/skills/pt-snap-memory-peak-breakdown/SKILL.md @@ -15,6 +15,20 @@ Use only an existing standalone SnapshotDB (`.db`) or an explicitly selected, complete validated dataset directory or `manifest.json`. Never open, import, inspect, or deserialize pickle (`.pkl` or `.pickle`) input. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [mandatory preflight](#mandatory-preflight), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#full-trace-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs Require one coherent database/device pair: diff --git a/skills/pt-snap-memory-peak-breakdown/references/output-recovery.md b/skills/pt-snap-memory-peak-breakdown/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/skills/pt-snap-memory-peak-breakdown/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-helper/SKILL.md b/src/pt_snap_cli/bundled_skills/pt-snap-helper/SKILL.md index 0fc07a8..ab6484b 100644 --- a/src/pt_snap_cli/bundled_skills/pt-snap-helper/SKILL.md +++ b/src/pt_snap_cli/bundled_skills/pt-snap-helper/SKILL.md @@ -20,6 +20,49 @@ Use this skill when: Do not use this skill to run leak, peak, or fragmentation analysis. Hand off to the matching diagnostic skill after routing. +## Normal route first + +1. Choose one goal/input row below; collection comes before later analysis. +2. Verify the selected skill's availability and actual host loading using the + prerequisite contract. Do not install, import or diagnose from this helper. +3. Hand off the explicit target/device and any complete reusable preflight + results; the diagnostic skill validates them before querying. + +Read only the recovery branch that applies: [custom/host availability](#interpret-availability-in-the-inspected-scope), +[missing focus](#missing-focus-target-with-sibling-candidates), or +[complete-output recovery](references/output-recovery.md). The full +[preflight](#shared-diagnostic-preflight-contract), [boundaries](#boundaries) and +[output](#output) remain mandatory; this route map is not a replacement. + +## Routing matrix + +Choose one next skill. If the goal is still ambiguous after this table, ask +the user; do not start all diagnostic skills. + +Apply the capture row first when capture is the current stage, including a +capture-then-analysis request. The missing-CLI analysis cells apply when the +user is ready for analysis, not to the earlier collection stage. Every handoff +requires host-loading evidence; a next-skill name alone is not a completed handoff. + +| User goal | Existing SnapshotDB | Only pickle | `pt-snap` missing | Need Ascend NPU capture | +| --- | --- | --- | --- | --- | +| Install or verify the CLI | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | +| Collect an Ascend NPU pickle | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | +| Leak / live allocations at end of trace | `pt-snap-memory-leak` | Trusted import first, then leak | `pt-snap-setup` first | Collect first, then trusted import, then leak | +| What was live at a peak event | `pt-snap-memory-peak-breakdown` | Trusted import first, then peak | `pt-snap-setup` first | Collect first, then trusted import, then peak | +| Allocator gaps / reserved-pool pressure | `pt-snap-memory-fragmentation` | Trusted import first, then fragmentation | `pt-snap-setup` first | Collect first, then trusted import, then fragmentation | + +Exact next-skill names: + +- `pt-snap-setup` +- `pt-snap-ascend-npu-collect` +- `pt-snap-memory-leak` +- `pt-snap-memory-peak-breakdown` +- `pt-snap-memory-fragmentation` + +CSV, SVG, and other visualization files are not SnapshotDB input. If the user +only has those, stop and ask for a `.db` or a trusted pickle. + ## Prerequisite: check skill availability If `pt-snap` is on PATH, inspect bundled skill status with JSON: @@ -210,35 +253,6 @@ report gap/coverage; one query/report cumulative 100000 fetched/output rows /64 serialized values is NOT an RSS ceiling. Budget failure is no partial global proof. This helper still only hands off; it does not execute analysis queries. -## Routing matrix - -Choose one next skill. If the goal is still ambiguous after this table, ask -the user; do not start all diagnostic skills. - -Apply the capture row first when capture is the current stage, including a -capture-then-analysis request. The missing-CLI analysis cells apply when the -user is ready for analysis, not to the earlier collection stage. Every handoff -requires host-loading evidence; a next-skill name alone is not a completed handoff. - -| User goal | Existing SnapshotDB | Only pickle | `pt-snap` missing | Need Ascend NPU capture | -| --- | --- | --- | --- | --- | -| Install or verify the CLI | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | `pt-snap-setup` | -| Collect an Ascend NPU pickle | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | `pt-snap-ascend-npu-collect` | -| Leak / live allocations at end of trace | `pt-snap-memory-leak` | Trusted import first, then leak | `pt-snap-setup` first | Collect first, then trusted import, then leak | -| What was live at a peak event | `pt-snap-memory-peak-breakdown` | Trusted import first, then peak | `pt-snap-setup` first | Collect first, then trusted import, then peak | -| Allocator gaps / reserved-pool pressure | `pt-snap-memory-fragmentation` | Trusted import first, then fragmentation | `pt-snap-setup` first | Collect first, then trusted import, then fragmentation | - -Exact next-skill names: - -- `pt-snap-setup` -- `pt-snap-ascend-npu-collect` -- `pt-snap-memory-leak` -- `pt-snap-memory-peak-breakdown` -- `pt-snap-memory-fragmentation` - -CSV, SVG, and other visualization files are not SnapshotDB input. If the user -only has those, stop and ask for a `.db` or a trusted pickle. - ### Existing SnapshotDB A standalone SnapshotDB is a SQLite database (typically `.db`) with a diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-helper/references/output-recovery.md b/src/pt_snap_cli/bundled_skills/pt-snap-helper/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/src/pt_snap_cli/bundled_skills/pt-snap-helper/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/SKILL.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/SKILL.md index 488cf5c..e0577a9 100644 --- a/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/SKILL.md +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/SKILL.md @@ -17,6 +17,20 @@ largest contiguous free region. Therefore, `reserved - active` is not pure fragmentation, and no definitive fragmentation ratio or OOM root cause can be derived from this workflow. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [preflight](#prerequisite-phase), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#diagnostic-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs - A SnapshotDB path, supplied explicitly or resolved from the current diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/references/output-recovery.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-fragmentation/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/SKILL.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/SKILL.md index 0e25502..9cdf92d 100644 --- a/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/SKILL.md +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/SKILL.md @@ -11,6 +11,20 @@ Use this skill to diagnose memory that remains live in a PyTorch SnapshotDB and This is an evidence-gathering workflow. A single snapshot cannot confirm a leak. An allocation without a recorded free event is a candidate that was still live when tracing ended, not proof that it can never be released. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [preflight](#prerequisite-phase), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#diagnostic-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs - A SnapshotDB path, either supplied explicitly or resolved by the current `pt-snap focus`. diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/references/output-recovery.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-leak/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/SKILL.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/SKILL.md index 791ec41..b0c12e4 100644 --- a/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/SKILL.md +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/SKILL.md @@ -15,6 +15,20 @@ Use only an existing standalone SnapshotDB (`.db`) or an explicitly selected, complete validated dataset directory or `manifest.json`. Never open, import, inspect, or deserialize pickle (`.pkl` or `.pickle`) input. +## Normal path and reading order + +1. Validate the explicit DB/device and [required inputs](#required-inputs). +2. Complete [mandatory preflight](#mandatory-preflight), reusing only full, current + capabilities/overview results. This applies even without helper routing. +3. Follow the [bounded evidence workflow](#full-trace-workflow), then the output and + [guardrails](#guardrails). Separate observations, inference and unknowns. + +The full contracts below remain mandatory. Dataset and compact-stack sections +apply only to those inputs/options; missing-focus recovery applies only to a +missing effective target. If a host tool truncates JSON, read +[complete-output recovery](references/output-recovery.md) before proceeding; +do not bypass preflight or write files to recover output. + ## Required Inputs Require one coherent database/device pair: diff --git a/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/references/output-recovery.md b/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/references/output-recovery.md new file mode 100644 index 0000000..8c0d366 --- /dev/null +++ b/src/pt_snap_cli/bundled_skills/pt-snap-memory-peak-breakdown/references/output-recovery.md @@ -0,0 +1,56 @@ +# Recover complete JSON after tool truncation + +Read this only when the host tool omitted command output. Only retry probes +that the calling skill is allowed to execute: the helper passes this procedure +to the selected diagnostic skill; it does not run diagnostic preflight itself. Transport truncation +is different from query `has_more` / `truncated` or `stack_truncated`; retrieving +the full JSON does not make a capped evidence set complete. + +1. Keep the exact command, CLI/Python environment, resolved target/device and + dataset fingerprint. Wait for the process to finish; retain exit status, + stdout and stderr separately. Partial live output is not a successful result. +2. If the host retains complete output, use its output-reading/continuation tool + to read every byte range from that same invocation, with no gaps or overlaps. + Reassemble in host memory and parse the **whole** stdout with `json.loads`; + reject missing/trailing bytes. Do not mix chunks from different invocations. +3. If the host discarded bytes, rerun only the missing/stale read-only probe + with a larger output budget. There is no pt-snap flag to increase a host's + output budget. Optionally remove JSON whitespace in memory using the code + below in the already selected Python environment. This preserves every key, + template, schema, value and completeness flag; it is not a catalog summary. +4. Check exit status and stderr, then apply the skill's full preflight to the + complete object: `ok`, CLI version, all required template contracts, resolved + absolute path/device, `overview.import_metadata.status` and dataset identity + when applicable. A successfully parsed JSON object alone is not admission. + Revalidate identity/freshness after a retry. Do not reuse a summary or a + truncated display as though it were the original complete object. +5. If the host still cannot expose the complete result, stop diagnosis and state + which probe is incomplete; ask for a tool/output-budget change. Do not use + `head`, `grep`, template-info substitution, raw SQL, file redirection or `tee` + to bypass the full-result requirement. Do not write scratch/report files. + +Run this as a Python snippet through the host's in-memory execution support +(or `python -c` with the same code); do not save it as a script. The default +command retries capabilities. To retry overview instead, replace `command` +with `["pt-snap", "overview", validated_absolute_db_path, "--json"]`, using an +already validated path string, not shell interpolation. Do not switch Python +or CLI installations. The caller must still request enough output budget or +retrieve every retained chunk of the final, minified JSON. + +```python +import json +import subprocess +import sys + +command = ["pt-snap", "capabilities", "--json"] +result = subprocess.run(command, capture_output=True, text=True, check=False) +sys.stderr.write(result.stderr) +if result.returncode: + raise SystemExit(result.returncode) +payload = json.loads(result.stdout) +print(json.dumps(payload, ensure_ascii=True, separators=(",", ":"))) +``` + +This buffers the complete probe in memory; it is not streaming and has no fixed +memory/output-byte ceiling. It does not reduce semantic data, query work or +result-row caps. Never apply it to import/install/collection to obtain a retry. diff --git a/tests/core/test_split_service.py b/tests/core/test_split_service.py index 34bc114..44c85d2 100644 --- a/tests/core/test_split_service.py +++ b/tests/core/test_split_service.py @@ -611,18 +611,22 @@ def test_stage_identity_closes_descriptor_when_fstat_fails( def recording_open(*args: object) -> int: descriptor = real_open(*args) # type: ignore[arg-type] - descriptors.append(descriptor) + if args[0] == tmp_path: + descriptors.append(descriptor) return descriptor - monkeypatch.setattr(split_module.os, "open", recording_open) - monkeypatch.setattr( - split_module.os, - "fstat", - lambda descriptor: (_ for _ in ()).throw(OSError("fstat failed")), - ) + def fail_stage_fstat(descriptor: int) -> object: + if descriptor in descriptors: + raise OSError("fstat failed") + return real_fstat(descriptor) with pytest.raises(OSError, match="fstat failed"): - SplitService._stage_identity(tmp_path) + # os is a process-wide module: do not inject faults into pytest's own + # file inspection, and restore it before pytest handles the exception. + with monkeypatch.context() as patch: + patch.setattr(split_module.os, "open", recording_open) + patch.setattr(split_module.os, "fstat", fail_stage_fstat) + SplitService._stage_identity(tmp_path) assert len(descriptors) == 1 with pytest.raises(OSError): diff --git a/tests/skills/test_helper_availability.py b/tests/skills/test_helper_availability.py index f34218e..2291308 100644 --- a/tests/skills/test_helper_availability.py +++ b/tests/skills/test_helper_availability.py @@ -1,6 +1,7 @@ """Availability instructions and the real CLI evidence used by the helper.""" import json +import shutil from pathlib import Path import pytest @@ -60,8 +61,7 @@ def test_custom_install_status_is_scoped_to_inspected_directory( if custom_status == "outdated": skill_file.write_text("older skill instructions\n", encoding="utf-8") elif custom_status == "missing": - skill_file.unlink() - skill_file.parent.rmdir() + shutil.rmtree(skill_file.parent) default = runner.invoke(app, ["skill", "list", "--json"]) assert default.exit_code == 0, default.output diff --git a/tests/test_docs_onboarding.py b/tests/test_docs_onboarding.py new file mode 100644 index 0000000..376394f --- /dev/null +++ b/tests/test_docs_onboarding.py @@ -0,0 +1,246 @@ +"""First-use docs and recovery examples, using synthetic inputs only.""" + +import json +import re +import shlex +import sqlite3 +import subprocess +from contextlib import closing +from pathlib import Path +from urllib.parse import unquote, urlsplit + +import pytest +from typer.testing import CliRunner + +from pt_snap_cli.cli import app + +ROOT = Path(__file__).resolve().parents[1] +LANGUAGES = ("en", "zh") +SKILLS = ( + "pt-snap-helper", + "pt-snap-memory-leak", + "pt-snap-memory-peak-breakdown", + "pt-snap-memory-fragmentation", +) +DOCS = ( + "README.md", + "README_zh.md", + "docs/README.md", + *(f"docs/{lang}/{topic}.md" for lang in LANGUAGES for topic in ("quickstart", "skills")), + *(f"skills/{skill}/SKILL.md" for skill in SKILLS), + *(f"skills/{skill}/references/output-recovery.md" for skill in SKILLS), +) + + +def _read(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def _blocks(text: str, language: str) -> list[str]: + return re.findall(rf"```{language}\n(.*?)```", text, re.S) + + +def _anchors(text: str) -> set[str]: + # GitHub heading anchors retain Unicode letters but drop punctuation. + headings = re.findall(r"^#{1,6} (.+)$", text, re.M) + return { + re.sub(r"[^\w\- ]", "", heading.lower()).replace(" ", "-") for heading in headings + } | set(re.findall(r' None: + for target in re.findall(r"\[[^\]\n]+\]\(([^\s)]+)\)", _read(path)): + parts = urlsplit(target) + if parts.scheme or parts.netloc: + continue + destination = (ROOT / path).parent / unquote(parts.path) if parts.path else ROOT / path + assert destination.exists(), (path, target) + if parts.fragment and destination.suffix == ".md": + assert unquote(parts.fragment) in _anchors(destination.read_text(encoding="utf-8")), ( + path, + target, + ) + + +@pytest.mark.parametrize("language", LANGUAGES) +def test_first_report_precedes_advanced_formats_with_nearby_state_boundaries(language: str) -> None: + text = _read(f"docs/{language}/quickstart.md") + report = text.index("pt-snap report peak-memory snapshot.pkl.db") + advanced = text.index("## Advanced Input Options" if language == "en" else "## 高级输入选项") + assert report < advanced + before_report = text[:report] + for term in ( + "Python 3.10", + "TorchNPU", + "torch_cuda_memory.html", + "pt-snap-ascend-npu-collect/SKILL.md", + "manifest.json", + ".pt-snap/focus.json", + "--no-focus", + "--output-dir", + "import_metadata.status", + "metadata_missing", + "1048576 bytes", + "allocated", + "active", + "reserved", + ): + assert term in before_report + assert "included_bytes" in text and "--limit 20" in text + assert "no-replace" in before_report and "--force" in before_report + + +def test_bilingual_quickstart_commands_and_example_are_identical() -> None: + texts = [_read(f"docs/{language}/quickstart.md") for language in LANGUAGES] + assert _blocks(texts[0], "bash") == _blocks(texts[1], "bash") + assert _blocks(texts[0], "json") == _blocks(texts[1], "json") + for path in ("README.md", "README_zh.md"): + text = _read(path) + assert "AGENTS.md#development-and-checks" in text + assert "basedpyright" in text + assert "python -m basedpyright" in _read("AGENTS.md") + + +@pytest.mark.parametrize("language", LANGUAGES) +def test_agent_normal_install_precedes_exception_navigation(language: str) -> None: + text = _read(f"docs/{language}/skills.md") + install = text.index("pt-snap skill install\n") + verify = text.index("pt-snap skill list --json") + custom = text.index("## Destinations" if language == "en" else "## 安装位置") + absent = text.index("## Collection when" if language == "en" else "## CLI 缺失") + assert install < verify < custom < absent + for term in ("pt-snap-helper", "installed", "100–700", "output-recovery.md"): + assert term in text + + +@pytest.fixture +def example_db(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(Path, "home", lambda: tmp_path) + monkeypatch.delenv("PT_SNAP_DB_PATH", raising=False) + path = tmp_path / "snapshot.pkl.db" + mib = 1048576 + with closing(sqlite3.connect(path)) as conn, conn: + conn.executescript(""" + CREATE TABLE dictionary (`table` TEXT, `column` TEXT, `key` TEXT, `value` TEXT); + CREATE TABLE trace_entry_0 ( + id INTEGER PRIMARY KEY, action INTEGER, address INTEGER, size INTEGER, + stream INTEGER, allocated INTEGER, active INTEGER, reserved INTEGER, + callstack TEXT + ); + CREATE TABLE block_0 ( + id INTEGER PRIMARY KEY, address INTEGER, size INTEGER, + requestedSize INTEGER, state INTEGER, allocEventId INTEGER, freeEventId INTEGER + ); + """) + # Allocate 2+6 MiB, request a deferred 2 MiB free, then allocate another + # 2 MiB. Earliest allocated/reserved peaks differ from the active peak. + conn.executemany( + "INSERT INTO trace_entry_0 VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?)", + [ + (9, 0, 100, 2 * mib, 2 * mib, 2 * mib, 8 * mib, "first"), + (10, 0, 200, 6 * mib, 8 * mib, 8 * mib, 16 * mib, "second"), + (11, 1, 100, 2 * mib, 6 * mib, 8 * mib, 16 * mib, "free"), + (12, 0, 300, 2 * mib, 8 * mib, 10 * mib, 16 * mib, "third"), + ], + ) + conn.executemany( + "INSERT INTO block_0 VALUES (?, ?, ?, ?, ?, ?, -1)", + [ + (1, 100, 2 * mib, 2 * mib, 0, 9), + (2, 200, 6 * mib, 6 * mib, 1, 10), + (3, 300, 2 * mib, 2 * mib, 1, 12), + ], + ) + return path + + +@pytest.mark.parametrize("language", LANGUAGES) +def test_documented_read_only_path_reaches_report(example_db: Path, language: str) -> None: + text = _read(f"docs/{language}/quickstart.md") + commands = [ + shlex.split(line)[1:] + for block in _blocks(text, "bash") + for line in block.strip().splitlines() + if line.startswith( + ("pt-snap overview ", "pt-snap query ", "pt-snap report peak-memory snapshot") + ) + ] + before = example_db.read_bytes() + results = [CliRunner().invoke(app, command) for command in commands] + assert len(results) == 3 + assert all(result.exit_code == 0 for result in results), [result.output for result in results] + overview = json.loads(results[0].stdout) + assert overview["import_metadata"]["status"] == "unavailable" + assert overview["import_metadata"]["reason"] == "metadata_missing" + expected_peak = json.loads(_blocks(text, "json")[0]) + assert json.loads(results[1].stdout)["rows"] == [expected_peak] + assert "Peak memory report" in results[2].stdout + assert "Event ID: 12" in results[2].stdout + assert "Active memory by callstack:" in results[2].stdout + assert "100.0000%" in results[2].stdout + assert example_db.read_bytes() == before + assert not (example_db.parent / ".pt-snap").exists() + + +@pytest.mark.parametrize("skill", SKILLS) +def test_recovery_reference_is_packaged_identically_and_preserves_complete_json(skill: str) -> None: + relative = f"{skill}/references/output-recovery.md" + assert _read(f"skills/{relative}") == _read(f"src/pt_snap_cli/bundled_skills/{relative}") + assert "references/output-recovery.md" in _read(f"skills/{skill}/SKILL.md") + recovery = _read(f"skills/{relative}") + for requirement in ( + "stdout and stderr separately", + "no gaps or overlaps", + "overview.import_metadata.status", + "stop diagnosis", + "Do not write scratch/report files", + ): + assert requirement in recovery + + +@pytest.mark.parametrize("outcome", ("complete", "truncated", "failed")) +def test_documented_in_memory_recovery_does_not_hide_failure_or_drop_fields( + outcome: str, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + code = _blocks(_read("skills/pt-snap-helper/references/output-recovery.md"), "python")[0] + payload = { + "ok": True, + "cli_version": "test", + "templates": [{"name": "memory_peak", "output_schema": [{"units": "bytes"}]}], + "skills": [], + "unknown_future_field": {"preserve": "完整"}, + } + stdout = json.dumps(payload, indent=2) + if outcome == "truncated": + stdout = stdout[:-4] + stderr = "probe failed" if outcome == "failed" else "" + calls = [] + + def run(command: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]: + calls.append((command, kwargs)) + return subprocess.CompletedProcess(command, 2 if outcome == "failed" else 0, stdout, stderr) + + monkeypatch.setattr(subprocess, "run", run) + if outcome == "complete": + exec(code, {}) + captured = capsys.readouterr() + assert json.loads(captured.out) == payload + assert len(captured.out) < len(stdout) + elif outcome == "truncated": + with pytest.raises(json.JSONDecodeError): + exec(code, {}) + assert capsys.readouterr().out == "" + else: + with pytest.raises(SystemExit) as error: + exec(code, {}) + assert error.value.code == 2 + captured = capsys.readouterr() + assert captured.out == "" and captured.err == stderr + assert calls == [ + ( + ["pt-snap", "capabilities", "--json"], + {"capture_output": True, "text": True, "check": False}, + ) + ]