Skip to content

Commit 3c71a4c

Browse files
quic-boyucclaude
andcommitted
observatory: add visualize subcommand, --json-only flag, and USAGE.md
Surfaces the inherent 2-step design (runtime collection → JSON, JSON → HTML) explicitly in the CLI. Adds a `visualize` subcommand that converts an existing JSON to HTML without re-running the export script, and a `--json-only` flag for CI/storage use cases. Fixes export order so JSON is always written before HTML. Adds USAGE.md covering zero-config e2e, two-step workflow, lens config, manual collection points, and demo script modes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fe373d3 commit 3c71a4c

2 files changed

Lines changed: 438 additions & 36 deletions

File tree

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Observatory CLI Usage Guide
2+
3+
The Observatory CLI wraps any ExecuTorch export script in an Observatory context,
4+
automatically collecting graph snapshots and accuracy metrics at each compilation stage.
5+
6+
## 1. Zero-Config E2E Workflow
7+
8+
The simplest invocation: point the CLI at your script and pass its arguments through.
9+
The CLI infers the output directory from the script's `-a`/`--artifact` or `-o`/`--output_dir` flag.
10+
11+
```bash
12+
python -m backends.qualcomm.debugger.observatory.cli \
13+
examples/qualcomm/oss_scripts/swin_v2_t.py \
14+
--model SM8650 -b ./build-android -d imagenet-mini/val -a ./swin_v2_t
15+
```
16+
17+
Output (inferred from `-a ./swin_v2_t`):
18+
- `./swin_v2_t/observatory_report.html` — interactive report
19+
- `./swin_v2_t/observatory_report.json` — raw data for later re-analysis
20+
21+
To set paths explicitly:
22+
23+
```bash
24+
python -m backends.qualcomm.debugger.observatory.cli \
25+
--report-html /tmp/obs/report.html \
26+
--report-json /tmp/obs/report.json \
27+
--report-title "Swin V2-T Qualcomm" \
28+
examples/qualcomm/oss_scripts/swin_v2_t.py \
29+
--model SM8650 -b ./build-android -d imagenet-mini/val -a ./swin_v2_t
30+
```
31+
32+
## 2. JSON-Only Export (CI / Storage)
33+
34+
Use `--json-only` to collect data and export only the raw JSON, skipping HTML generation.
35+
This is useful in CI pipelines where you want to store a compact artifact and generate
36+
the HTML report locally later.
37+
38+
```bash
39+
python -m backends.qualcomm.debugger.observatory.cli \
40+
--json-only \
41+
--report-json /tmp/obs/report.json \
42+
examples/qualcomm/oss_scripts/swin_v2_t.py \
43+
--model SM8650 -b ./build-android -d imagenet-mini/val -a ./swin_v2_t
44+
```
45+
46+
The JSON file contains all raw lens digests and session data. No HTML is written.
47+
48+
## 3. Convert JSON to HTML (Visualize Mode)
49+
50+
Use the `visualize` subcommand to convert an existing JSON file to HTML without
51+
re-running the export script. This re-runs the analysis phase (lens `analyze()` methods)
52+
against the persisted data, so HTML reports can be updated after lens code changes.
53+
54+
```bash
55+
python -m backends.qualcomm.debugger.observatory.cli visualize \
56+
--input /tmp/obs/report.json \
57+
--output /tmp/obs/report.html \
58+
--title "Swin V2-T Qualcomm"
59+
```
60+
61+
Options:
62+
- `--input` / `-i` — path to the raw JSON file (required)
63+
- `--output` / `-o` — path for the generated HTML file (required)
64+
- `--title` — report title shown in the HTML header (default: "Observatory Report")
65+
66+
## 4. Two-Step Workflow (CI collect, local visualize)
67+
68+
Combine steps 2 and 3 for a CI-collect / local-visualize pattern:
69+
70+
**Step 1 — CI: collect and export JSON only**
71+
```bash
72+
python -m backends.qualcomm.debugger.observatory.cli \
73+
--json-only --report-json artifacts/report.json \
74+
my_export_script.py --output_dir artifacts/
75+
```
76+
77+
**Step 2 — Local: convert JSON to HTML**
78+
```bash
79+
python -m backends.qualcomm.debugger.observatory.cli visualize \
80+
--input artifacts/report.json \
81+
--output artifacts/report.html \
82+
--title "My Model Report"
83+
```
84+
85+
This separates the expensive on-device execution (Step 1) from the interactive
86+
visualization (Step 2), which can be re-run any number of times.
87+
88+
## 5. Disabling Lenses
89+
90+
### Disable accuracy collection (faster runs, no accuracy metrics)
91+
92+
```bash
93+
python -m backends.qualcomm.debugger.observatory.cli \
94+
--no-accuracy \
95+
my_script.py [script_args...]
96+
```
97+
98+
### Skip all report output (collect only, no files written)
99+
100+
```bash
101+
python -m backends.qualcomm.debugger.observatory.cli \
102+
--no-report \
103+
my_script.py [script_args...]
104+
```
105+
106+
### Disable lenses via config in custom scripts
107+
108+
When using the Observatory Python API directly, pass a config dict to
109+
`enable_context()` or `export_html_report()`:
110+
111+
```python
112+
from executorch.backends.qualcomm.debugger.observatory import Observatory
113+
114+
config = {
115+
"accuracy": {"enabled": False},
116+
"per_layer_accuracy": {"enabled": False},
117+
}
118+
119+
with Observatory.enable_context(config=config):
120+
# ... your export code ...
121+
122+
Observatory.export_html_report("report.html", config=config)
123+
```
124+
125+
Config keys correspond to lens names returned by `lens.get_name()`. Each lens
126+
checks `config.get(lens_name, {}).get("enabled", True)` during setup.
127+
128+
## 6. Manual Observation Collection Points
129+
130+
You can insert `Observatory.collect()` calls anywhere in your code to capture
131+
intermediate graph states. This is useful for debugging pass transforms or
132+
custom lowering steps.
133+
134+
### Basic usage
135+
136+
```python
137+
import torch
138+
from executorch.backends.qualcomm.debugger.observatory import Observatory
139+
140+
model = MyModel().eval()
141+
graph = torch.fx.symbolic_trace(model)
142+
143+
Observatory.clear()
144+
with Observatory.enable_context():
145+
Observatory.collect("original", graph)
146+
147+
# Apply a pass
148+
transformed = my_pass(graph)
149+
Observatory.collect("after_my_pass", transformed)
150+
151+
Observatory.export_html_report("pass_debug.html")
152+
Observatory.export_json("pass_debug.json")
153+
```
154+
155+
### Pass transform debugging
156+
157+
To compare graphs before and after a specific pass:
158+
159+
```python
160+
with Observatory.enable_context():
161+
for name, module in model.named_modules():
162+
before = torch.fx.symbolic_trace(module)
163+
Observatory.collect(f"{name}/before", before)
164+
165+
after = my_transform(before)
166+
Observatory.collect(f"{name}/after", after)
167+
```
168+
169+
### Inside the CLI-wrapped script (zero-code-change)
170+
171+
When running via the CLI, `Observatory.enable_context()` is already active.
172+
You can add collection points to your script without any setup:
173+
174+
```python
175+
# In your export script (e.g., my_model.py):
176+
from executorch.backends.qualcomm.debugger.observatory import Observatory
177+
178+
# This fires only when Observatory context is active (i.e., when run via CLI).
179+
# It is a no-op otherwise.
180+
Observatory.collect("pre_quantize", exported_program)
181+
```
182+
183+
## 7. Demo Script Modes
184+
185+
The batch demo script (`scripts/generate_observatory_demo.py`) supports three modes:
186+
187+
### Default: run all jobs
188+
189+
```bash
190+
python scripts/generate_observatory_demo.py \
191+
--xnn-models mv2 \
192+
--qualcomm-models mobilenet_v2 \
193+
--qnn-sdk-root /path/to/qairt/2.37.0
194+
```
195+
196+
Runs each job, writes HTML + JSON reports, and refreshes `index.html`.
197+
198+
### `--plan-only`: register without running
199+
200+
```bash
201+
python scripts/generate_observatory_demo.py \
202+
--plan-only \
203+
--xnn-models mv2,resnet18 \
204+
--qualcomm-models mobilenet_v2,roberta
205+
```
206+
207+
Creates output directories and writes `manifest.json` + `index.html` with all
208+
jobs listed as `"planned"` status. No scripts are executed. Useful for previewing
209+
the job plan or pre-creating the index before a long run.
210+
211+
### `--visualize-only`: re-render HTML from existing JSON
212+
213+
```bash
214+
python scripts/generate_observatory_demo.py --visualize-only
215+
```
216+
217+
Reads `manifest.json`, calls `cli visualize` for each job that has an existing
218+
JSON file, and refreshes `index.html`. Jobs without a JSON file are skipped with
219+
a warning. Requires a prior successful run (or manually placed JSON files).
220+
221+
Use this after updating lens code to regenerate all HTML reports without
222+
re-running the expensive export scripts.
223+
224+
## 8. Quick Reference
225+
226+
| Scenario | Command |
227+
|----------|---------|
228+
| E2E single script | `cli script.py [script_args]` |
229+
| E2E with explicit paths | `cli --report-html X.html --report-json X.json script.py ...` |
230+
| JSON only (no HTML) | `cli --json-only --report-json X.json script.py ...` |
231+
| JSON → HTML | `cli visualize --input X.json --output X.html` |
232+
| No accuracy metrics | `cli --no-accuracy script.py ...` |
233+
| No output files | `cli --no-report script.py ...` |
234+
| Batch plan (no run) | `generate_observatory_demo.py --plan-only` |
235+
| Batch run | `generate_observatory_demo.py` |
236+
| Batch re-render HTML | `generate_observatory_demo.py --visualize-only` |
237+
| Single model batch | `generate_observatory_demo.py --xnn-models mv2 --qualcomm-models mobilenet_v2` |

0 commit comments

Comments
 (0)