Skip to content

web: add self-contained static HTML timing report (web_save_report)#10087

Open
openroad-ci wants to merge 2 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:web-static
Open

web: add self-contained static HTML timing report (web_save_report)#10087
openroad-ci wants to merge 2 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:web-static

Conversation

@openroad-ci
Copy link
Copy Markdown
Collaborator

Summary

Add a web_save_report Tcl command that generates a standalone HTML timing report reusing the exact same JS/CSS as the live web viewer.

Type of Change

  • New feature

Impact

Architecture:
The static report substitutes the WebSocket server with a cache
layer. WebSocketManager gains a fromCache(cache) factory that
serves pre-computed responses from window.__STATIC_CACHE__
embedded in the HTML. Cached request types include tech, bounds,
timing_report, slack_histogram, chart_filters, and tile PNGs.
Uncached requests (select, tcl_eval, etc.) reject gracefully.

All 18 JS source files plus a vendored GoldenLayout bundle are
concatenated by embed_report_assets.py into a single <script>
block. Each file is wrapped in an IIFE to isolate const/let
declarations; exported symbols are forwarded to outer-scope vars.

Layout view:
Tiles are pre-rendered at a fixed Leaflet zoom level using the
existing generateTile() infrastructure. The tile layer returns
data URIs from cache instead of creating blob URLs, ensuring
compatibility with file:// origins. Leaflet zoom is locked to
the cached level; pan is allowed.

Timing path overlay:
Per-path overlay PNGs are pre-rendered via renderOverlayPng()
(colored rects + flight lines on a transparent background).
When a timing path is selected, the cache drives a Leaflet
ImageOverlay to display the highlight.

Display controls, timing tables, slack histograms, and clock tree all work identically to the live viewer since they run the same widget code against cached data.

Files:

  • websocket-manager.js: fromCache(), _cacheRequest(), isStaticMode
  • main.js: STATIC_CACHE detection, zoom lock, overlay setup
  • embed_report_assets.py: IIFE wrapping, export-block handling
  • web.cpp: saveReport() with tile/overlay/JSON cache generation
  • web.tcl/web.i/web.h: web_save_report command plumbing
  • tile_generator.cpp/h: renderOverlayPng() for path highlights
  • websocket-tile-layer.js: data URI support alongside blob URLs
  • golden-layout.min.js: vendored ESM bundle from esm.sh
  • BUILD/CMakeLists.txt: embed pipeline with all JS files

Verification

  • I have verified that the local build succeeds (./etc/Build.sh).
  • I have run the relevant tests and they pass.
  • My code follows the repository's formatting guidelines.
  • I have signed my commits (DCO).

Related Issues

[Link issues here]

Add a `web_save_report` Tcl command that generates a standalone HTML
timing report reusing the exact same JS/CSS as the live web viewer.

Architecture:
  The static report substitutes the WebSocket server with a cache
  layer. WebSocketManager gains a `fromCache(cache)` factory that
  serves pre-computed responses from `window.__STATIC_CACHE__`
  embedded in the HTML. Cached request types include tech, bounds,
  timing_report, slack_histogram, chart_filters, and tile PNGs.
  Uncached requests (select, tcl_eval, etc.) reject gracefully.

  All 18 JS source files plus a vendored GoldenLayout bundle are
  concatenated by embed_report_assets.py into a single <script>
  block. Each file is wrapped in an IIFE to isolate const/let
  declarations; exported symbols are forwarded to outer-scope vars.

Layout view:
  Tiles are pre-rendered at a fixed Leaflet zoom level using the
  existing generateTile() infrastructure. The tile layer returns
  data URIs from cache instead of creating blob URLs, ensuring
  compatibility with file:// origins. Leaflet zoom is locked to
  the cached level; pan is allowed.

Timing path overlay:
  Per-path overlay PNGs are pre-rendered via renderOverlayPng()
  (colored rects + flight lines on a transparent background).
  When a timing path is selected, the cache drives a Leaflet
  ImageOverlay to display the highlight.

Display controls, timing tables, slack histograms, and clock tree
all work identically to the live viewer since they run the same
widget code against cached data.

Files:
  - websocket-manager.js: fromCache(), _cacheRequest(), isStaticMode
  - main.js: __STATIC_CACHE__ detection, zoom lock, overlay setup
  - embed_report_assets.py: IIFE wrapping, export-block handling
  - web.cpp: saveReport() with tile/overlay/JSON cache generation
  - web.tcl/web.i/web.h: web_save_report command plumbing
  - tile_generator.cpp/h: renderOverlayPng() for path highlights
  - websocket-tile-layer.js: data URI support alongside blob URLs
  - golden-layout.min.js: vendored ESM bundle from esm.sh
  - BUILD/CMakeLists.txt: embed pipeline with all JS files
Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the ability to generate a self-contained HTML timing report. This involves embedding JavaScript and CSS assets into a C++ file, serializing various timing data (paths, histograms, filters), and rendering map tiles and path overlays directly into the HTML. The WebSocketManager and related JavaScript components were updated to support a static cache mode for these reports. Review comments highlight the need to value-initialize TileVisibility objects in src/web/src/web.cpp and src/web/src/tile_generator.cpp to prevent non-deterministic behavior. Additionally, the output file for the report should be opened earlier in src/web/src/web.cpp to improve efficiency, and layer names embedded in JavaScript string literals in the same file require proper escaping to avoid invalid JavaScript. Finally, file operations in src/web/src/embed_report_assets.py should explicitly specify UTF-8 encoding for better portability.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

clang-tidy review says "All clean, LGTM! 👍"

@maliberty
Copy link
Copy Markdown
Member

@oharboe please give it a try.

- Open output file before expensive rendering to fail fast on bad paths
- Escape tile cache keys with json_escape() for valid JavaScript output
- Specify encoding='utf-8' in embed_report_assets.py for portability

Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

clang-tidy review says "All clean, LGTM! 👍"

@oharboe
Copy link
Copy Markdown
Collaborator

oharboe commented Apr 9, 2026

$ git log -1
commit 30e70429ab3b72f525b7427e41b410cfdcf24af7 (HEAD)
Author: Matt Liberty <mliberty@precisioninno.com>
Date:   Wed Apr 8 18:59:11 2026 +0000

    web: harden saveReport with early file check, escaping, and encoding
    
    - Open output file before expensive rendering to fail fast on bad paths
    - Escape tile cache keys with json_escape() for valid JavaScript output
    - Specify encoding='utf-8' in embed_report_assets.py for portability
    
    Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
$ bazelisk run test/orfs/gcd:gcd_synth open_synth
[deleted]
+ exec ./test/orfs/gcd/make_gcd_synth_1_synth open_synth
ODB_FILE=./test/orfs/gcd/results/asap7/gcd/base/1_synth.odb ./openroad -no_init -threads 20  external/bazel-orfs++orfs_repositories+docker_orfs/OpenROAD-flow-scripts/flow/scripts/open.tcl
[Warning] Failed to create bazel runfiles: ERROR: external/rules_cc+/cc/runfiles/runfiles.cc(103): cannot find runfiles (argv0="/home/oyvind/OpenROAD-flow-scripts/tools/OpenROAD/tmp/test/orfs/gcd/_main/openroad")
application-specific initialization failed: 
% web_save_report -setup_paths 1000 -hold_paths 1000 test.html
invalid command name "web_save_report"
% 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants