Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
2a99352
Add fx_viewer in executorch
quic-boyuc Mar 11, 2026
46be9ce
Figure out plan for integration of observatory with fx_viewer
quic-boyuc Mar 12, 2026
1719b6e
fx_viewer: introduce state-driven JS runtime API and embed config
quic-boyuc Mar 17, 2026
41babb3
fx_viewer/examples: add unified API harness generator and testcases
quic-boyuc Mar 17, 2026
55e69f1
fx_viewer/examples: add per-layer accuracy demo and observatory calls…
quic-boyuc Mar 17, 2026
1d67726
fx_viewer/docs: document RFC API surface and runtime architecture
quic-boyuc Mar 17, 2026
0337eed
fx_viewer: harden viewer lifecycle and compare sync behavior
quic-boyuc Mar 17, 2026
a10c2ee
fx_viewer: simplify runtime layer mutation refresh paths
quic-boyuc Mar 17, 2026
7c74bec
fx_viewer: align comment and formatting style with existing codebase
quic-boyuc Mar 17, 2026
17704ba
fx_viewer: clean docs artifacts and restore merged README guidance
quic-boyuc Mar 17, 2026
9a212c6
fx_viewer: reduce runtime boilerplate with shared listener and UI hel…
quic-boyuc Mar 17, 2026
f9ecab3
fx_viewer/examples: add tutorial-style API learning ladder and mixed …
quic-boyuc Mar 17, 2026
537ecb3
observatory: scaffold RFC contracts and planning docs
quic-boyuc Mar 17, 2026
1c53d60
observatory: add core runtime, minimal lenses, and ETRecord auto-coll…
quic-boyuc Mar 17, 2026
4f46ad5
observatory(ui): split report runtime into topic JS template files
quic-boyuc Mar 17, 2026
6da102c
observatory: add demos, UI harness, and smoke tests
quic-boyuc Mar 17, 2026
96f61d3
Integrate fx_graph into observatory
quic-boyuc Mar 19, 2026
c76e022
observatory: safe HTML embedding, gzip compression, async resource bo…
quic-boyuc Mar 20, 2026
1b50549
observatory(ui): fix camera init and add viewer state cache
quic-boyuc Mar 20, 2026
51f3dcb
observatory: add zero-config CLI runner with pipeline graph and accur…
quic-boyuc Mar 23, 2026
f5eb871
observatory(accuracy): fix timing issue — lazy evaluator setup on fir…
quic-boyuc Mar 24, 2026
c5070eb
observatory(ui): auto-hide panels, theme sync, compare snap fix, full…
quic-boyuc Mar 24, 2026
521c798
observatory(accuracy): redesign Metric class, add MSE/AbsErr, per-sam…
quic-boyuc Mar 24, 2026
bbec37c
fx_viewer: redesign compare view DOM, fix layout bugs, add 5 UI fixes
quic-boyuc Mar 27, 2026
f984d69
fx_viewer: update JS 08 compare testcase with shared taskbar toggle; …
quic-boyuc Mar 31, 2026
7c84485
fx_viewer: JS templates refactor — JSDoc→README, XSS escaping, bounds…
quic-boyuc Mar 31, 2026
b4e004a
Refine and refactor fx_viewer
quic-boyuc Apr 1, 2026
4382c10
Integrate new fx_viewer compare view into observatory.
quic-boyuc Apr 2, 2026
53c0b5f
Refine observatory for xnnpack aot_compiler
quic-boyuc Apr 2, 2026
22fe404
fx_viewer/observatory: use from_node_root as primary cross-graph sync…
quic-boyuc Apr 3, 2026
f8e33c1
observatory: unify backend dataset fallback contract for accuracy lens
quic-boyuc Apr 3, 2026
f59aa7d
observatory: collect pre-edge inputs and rename edge output record
quic-boyuc Apr 3, 2026
d2cf5b2
fx_viewer: refine rendering, compare sync, and neighbor collection
quic-boyuc Apr 4, 2026
ef88048
fx_viewer: improve quantization-aware sync, field=value search, and m…
quic-boyuc Apr 4, 2026
c557586
fix(observatory/fx_viewer): prevent fullscreen on detached cached vie…
quic-boyuc Apr 4, 2026
4cf529c
fx_viewer/observatory: edge dialect targets, graph color lens, compar…
quic-boyuc Apr 4, 2026
2113428
fx_viewer/observatory: adjust minimap layout for usability
quic-boyuc Apr 5, 2026
570ada1
observatory: merge analyze-only graph layers and align graph_color de…
quic-boyuc Apr 6, 2026
e90e26a
observatory / fx_viewer: optimized coloring and theme
quic-boyuc Apr 6, 2026
b5b7732
Add per-layer accuracy lens with PSNR-based graph/table UI
quic-boyuc Apr 6, 2026
0e08d07
Add payload relayout API and observatory graph relayout integration
quic-boyuc Apr 6, 2026
aec0a40
Add per-metric per-layer accuracy graph layers and docs
quic-boyuc Apr 6, 2026
f2d3c70
observatory: add visualize subcommand, --json-only flag, and USAGE.md
quic-boyuc Apr 7, 2026
088cb49
fx_viewer: scale minimap node highlight sizes with minimapScale
quic-boyuc Apr 7, 2026
edf4605
observatory: restructure docs into user-friendly README + REFERENCE.md
quic-boyuc Apr 7, 2026
2e2fd3c
observatory: promote observatory & fx_viewer to shared devtools
quic-boyuc Apr 13, 2026
8e6be76
observatory: update document and cli options to feat new module struc…
quic-boyuc Apr 14, 2026
885605e
observatory: add observe_pass decorator and collect() name deduplication
quic-boyuc Apr 14, 2026
e6e42fb
observatory: default graph compare sync to sparse_match_key for per-l…
quic-boyuc Apr 14, 2026
23b566e
Bug Fixes
quic-boyuc Apr 14, 2026
5f62daf
Fix json float nan issue, simplify cli, prepare rfc draft
quic-boyuc Apr 14, 2026
3f0488c
fx_viewer: switch layout to fast-sugiyama with natural edge flow
quic-boyuc Apr 20, 2026
7d630f5
fx_viewer: fix layout issue with fast-sugiyaga layout
quic-boyuc Apr 20, 2026
8a92efc
observatory + fx_viewer: unify per-layer accuracy color scale, contra…
quic-boyuc Apr 20, 2026
a04dae4
Fix typo for --lens_recipe option, update readme and example
quic-boyuc Apr 21, 2026
6517525
Deleted out-dated RFC and test files
quic-boyuc Apr 21, 2026
5f25b92
Update readme and cli experience, remove previous edits on existing e…
quic-boyuc Apr 22, 2026
f59a113
Remove intermediate md files
quic-boyuc Apr 23, 2026
f3a085b
Handle both XNNPACK quantization import paths
quic-boyuc May 2, 2026
f02dd78
Adjust graph block size in Observatory template
quic-boyuc May 2, 2026
6a3afad
Add backend specific readme.md
quic-boyuc May 4, 2026
0936bca
Fix Observatory stack trace links
quic-boyuc May 5, 2026
ecaefd2
observatory: fix qnn accuracy lens hook
quic-boyuc May 5, 2026
d29ad3c
Fix compare graph layout refresh
quic-boyuc May 5, 2026
1dd5421
Improve compare info table readability
quic-boyuc May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions backends/qualcomm/debugger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,38 @@ Note: Files ending with `.bin ` do not support graph visualization in qairt_visu
For more details, visit the [QAIRT Visualizer](https://pypi.org/project/qairt-visualizer/).


# Observatory

A new, review-focused Observatory implementation is available under:

`backends/qualcomm/debugger/observatory`

Use the Observatory CLI to wrap any Qualcomm AOT export script. Use `--lens_recipe=accuracy`
to enable accuracy lenses.

```bash
python -m executorch.backends.qualcomm.debugger.observatory \
--output-html obs_report.html \
--lens_recipe=accuracy \
{original script and args}
```

For example:

```bash
python -m executorch.backends.qualcomm.debugger.observatory \
--output-html obs_report.html \
--lens_recipe=accuracy \
examples/qualcomm/oss_scripts/mobilevit_v2.py \
--backend htp --model SM8650 -d ./imagenet-mini-val/ -b build-android/ --compile_only
```

> **Note**: Qualcomm example scripts (e.g. `oss_scripts/roberta.py`) use only absolute imports
> and are run as plain scripts. The Observatory CLI auto-selects `runpy.run_path` for these since
> their directories do not contain `__init__.py`.

See `backends/qualcomm/debugger/observatory/README.md` for full documentation.

# ExecuTorch QNN Intermediate Output Debugger

ExecuTorch QNN Intermediate Output Debugger is a tool that helps users debug intermediate output accuracy by comparing CPU outputs with QNN outputs. This tool offers a variety of output formats and flexibility for users to define their own metrics when debugging.
Expand Down
146 changes: 146 additions & 0 deletions backends/qualcomm/debugger/observatory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Qualcomm Observatory CLI

Qualcomm-specific Observatory CLI that wraps `devtools/observatory` with QNN backend patches and
accuracy lenses. Requires a QNN SDK environment (source `$QNN_SDK_ROOT/bin/envsetup.sh` before
running on-device jobs).

## Usage

### Collection mode (default)

```bash
python -m executorch.backends.qualcomm.debugger.observatory \
[--output-html PATH] [--output-json PATH] SCRIPT [SCRIPT_ARGS...]
```

### With accuracy debugging

```bash
python -m executorch.backends.qualcomm.debugger.observatory \
--lens_recipe=accuracy \
[--output-html PATH] [--output-json PATH] \
SCRIPT [SCRIPT_ARGS...]
```

### Visualize mode (JSON → HTML, no re-execution)

```bash
python -m executorch.backends.qualcomm.debugger.observatory visualize \
--input-json report.json --output-html report.html
```

## Qualcomm examples

Qualcomm example scripts use only absolute imports and live in directories without `__init__.py`,
so the Observatory CLI runs them as plain scripts via `runpy.run_path` (no special invocation
needed).

### Vision model (ImageNet)

```bash
source $QNN_SDK_ROOT/bin/envsetup.sh

python -m executorch.backends.qualcomm.debugger.observatory \
--output-html /tmp/obs_vit/report.html \
--output-json /tmp/obs_vit/report.json \
--lens_recipe=accuracy \
examples/qualcomm/scripts/torchvision_vit.py \
-m SM8650 -b ./build-android \
--dataset imagenet-mini-val/ \
-H mlgtw-linux -s <device_serial> \
-a /tmp/obs_vit --seed 1126 --compile_only
```

### NLP model (Wikipedia sentences)

```bash
python -m executorch.backends.qualcomm.debugger.observatory \
--output-html /tmp/obs_roberta/report.html \
--lens_recipe=accuracy \
examples/qualcomm/oss_scripts/roberta.py \
-m SM8650 -b ./build-android \
-H mlgtw-linux -s <device_serial> \
-a /tmp/obs_roberta --compile_only
```

### Compile-only (no device required)

Add `--compile_only` to any Qualcomm script to export and lower without pushing to device.
This is useful for inspecting the compilation pipeline in CI or on a dev machine.

## Available example scripts

### `examples/qualcomm/scripts/` — vision models

| Script | Model |
|---|---|
| `torchvision_vit.py` | Vision Transformer |
| `mobilenet_v2.py` | MobileNetV2 |
| `mobilenet_v3.py` | MobileNetV3 |
| `inception_v3.py` | InceptionV3 |
| `inception_v4.py` | InceptionV4 |

Dataset: ImageNet (pass with `--dataset <path>` or `-d <path>`).

### `examples/qualcomm/oss_scripts/` — NLP/open-source models

| Script | Model |
|---|---|
| `roberta.py` | RoBERTa |
| `bert.py` | BERT |
| `albert.py` | ALBERT |
| `distilbert.py` | DistilBERT |
| `eurobert.py` | EuroBERT |

Dataset: Wikipedia sentences (`wikisent2.txt`). Pass with `-d <path>`.

Common flags: `-m <SOC_MODEL>` (e.g. `SM8650`), `-b <build_folder>`, `-H <host>`,
`-s <device_serial>`, `-a <artifact_dir>`, `--compile_only`.

## Accuracy lenses (`--lens_recipe=accuracy`)

Registers `AccuracyLens` and `PerLayerAccuracyLens` (with QNN dataset patches) on top of the
default `PipelineGraphCollectorLens`. These produce:

- Per-stage accuracy metrics (PSNR, cosine similarity, MSE, top-k)
- Per-layer accuracy heat-map overlaid on the graph
- Cross-stage diff labels in the left panel of the HTML report

QNN dataset patches (`lenses/qnn_dataset_patches.py`) wire the on-device inference output back
into the accuracy lens so metrics reflect true QNN outputs, not emulated CPU results.

## Two-step workflow

Collect on-device in CI, visualize locally without re-running:

```bash
# Step 1 — collect (e.g., in CI with device attached)
python -m executorch.backends.qualcomm.debugger.observatory \
--output-html /tmp/obs/report.html \
--output-json /tmp/obs/report.json \
examples/qualcomm/scripts/torchvision_vit.py \
-m SM8650 -b ./build-android -d imagenet-mini-val/ \
-H mlgtw-linux -s <device_serial> -a /tmp/obs

# Step 2 — re-generate HTML from JSON (e.g., locally after lens update)
python -m executorch.backends.qualcomm.debugger.observatory visualize \
--input-json /tmp/obs/report.json \
--output-html /tmp/obs/report_v2.html
```

## Backend patches

`lenses/qnn_patches.py` installs a monkey-patch on `ptq_calibrate` so the
`PipelineGraphCollectorLens` can intercept the QNN quantization calibration stage and capture
the graph at that point. The patch is active only while the Observatory context is open.

`lenses/qnn_dataset_patches.py` wires on-device inference results into `AccuracyLens` so that
accuracy metrics use real QNN outputs.

## See also

- `backends/qualcomm/debugger/README.md` — broader Qualcomm debugger overview (QAIRT visualizer,
intermediate output debugger)
- `devtools/observatory/README.md` — framework overview, Python API, custom lens guide
- `devtools/observatory/USAGE.md` — full CLI reference
- `devtools/observatory/lenses/LENSES.md` — built-in lens details
5 changes: 5 additions & 0 deletions backends/qualcomm/debugger/observatory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
9 changes: 9 additions & 0 deletions backends/qualcomm/debugger/observatory/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

from .cli import main

main()
81 changes: 81 additions & 0 deletions backends/qualcomm/debugger/observatory/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

"""Qualcomm Observatory CLI -- QNN-specific lens configuration.

Collection mode (default):
python -m executorch.backends.qualcomm.debugger.observatory \\
[--output-html PATH] [--output-json PATH] SCRIPT [SCRIPT_ARGS...]

With accuracy debugging:
python -m executorch.backends.qualcomm.debugger.observatory \\
--lens_recipe=accuracy SCRIPT [SCRIPT_ARGS...]

Visualize mode (JSON -> HTML):
python -m executorch.backends.qualcomm.debugger.observatory visualize \\
--input-json report.json --output-html report.html
"""

from __future__ import annotations

import sys


def main():
from executorch.devtools.observatory.cli import (
make_collect_parser,
make_visualize_parser,
run_observatory,
run_visualize,
)

if len(sys.argv) > 1 and sys.argv[1] == "visualize":
parser = make_visualize_parser()
args = parser.parse_args(sys.argv[2:])
run_visualize(args.input_json, args.output_html)
return

parser = make_collect_parser(
prog="python -m executorch.backends.qualcomm.debugger.observatory"
)
parser.add_argument(
"--lens_recipe",
choices=["accuracy"],
default=None,
help="Lens recipe to enable (e.g. accuracy)",
)
args = parser.parse_args(sys.argv[1:])

from executorch.devtools.observatory.observatory import Observatory
from executorch.devtools.observatory.lenses.pipeline_graph_collector import (
PipelineGraphCollectorLens,
)
from .lenses.qnn_patches import install_qnn_patches

Observatory.clear()
PipelineGraphCollectorLens.register_backend_patches(install_qnn_patches)
Observatory.register_lens(PipelineGraphCollectorLens)

if args.lens_recipe == "accuracy":
from executorch.devtools.observatory.lenses.accuracy import AccuracyLens
from .lenses.qnn_dataset_patches import install_qnn_dataset_patches

AccuracyLens.register_dataset_patches(install_qnn_dataset_patches)
Observatory.register_lens(AccuracyLens)

from executorch.devtools.observatory.lenses.per_layer_accuracy import (
PerLayerAccuracyLens,
)

Observatory.register_lens(PerLayerAccuracyLens)

run_observatory(
args.script, args.script_args, Observatory, args.output_html, args.output_json
)


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions backends/qualcomm/debugger/observatory/lenses/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright (c) Qualcomm Innovation Center, Inc.
# All rights reserved
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

"""QNN dataset patches for AccuracyLens.

Installs monkey-patches on executorch.examples.qualcomm.utils dataset functions
to capture targets and task type for accuracy evaluation.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from executorch.devtools.observatory.lenses.accuracy import AccuracyLens


def install_qnn_dataset_patches(cls: type[AccuracyLens]) -> None:
"""Install QNN dataset capture patches on AccuracyLens."""
try:
import executorch.examples.qualcomm.utils as utils_module

if hasattr(utils_module, "get_imagenet_dataset"):
original = utils_module.get_imagenet_dataset
cls._originals["get_imagenet_dataset"] = original

def patched_imagenet(*args, **kwargs):
inputs, targets = original(*args, **kwargs)
cls._captured_targets = targets
cls._task_type = "classification"
logging.info(
"[AccuracyLens] Captured ImageNet targets (%d samples)",
len(targets),
)
return inputs, targets

utils_module.get_imagenet_dataset = patched_imagenet
logging.info("[AccuracyLens] Installed patch: get_imagenet_dataset")

if hasattr(utils_module, "get_masked_language_model_dataset"):
original_mlm = utils_module.get_masked_language_model_dataset
cls._originals["get_masked_language_model_dataset"] = original_mlm

def patched_mlm(*args, **kwargs):
inputs, targets = original_mlm(*args, **kwargs)
cls._captured_targets = targets
cls._task_type = "mlm"
logging.info(
"[AccuracyLens] Captured MLM targets (%d samples)",
len(targets),
)
return inputs, targets

utils_module.get_masked_language_model_dataset = patched_mlm
logging.info(
"[AccuracyLens] Installed patch: get_masked_language_model_dataset"
)

def _uninstall():
try:
for key, orig in cls._originals.items():
if hasattr(utils_module, key):
setattr(utils_module, key, orig)
except Exception:
pass

cls._dataset_uninstallers.append(_uninstall)
except ImportError:
logging.debug(
"[AccuracyLens] qualcomm utils not available, skipping dataset patches"
)
Loading
Loading