web: add self-contained static HTML timing report (web_save_report)#10087
Conversation
There was a problem hiding this comment.
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.
|
clang-tidy review says "All clean, LGTM! 👍" |
|
@oharboe please give it a try. |
|
clang-tidy review says "All clean, LGTM! 👍" |
|
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>
- 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>
The npm_link_all_packages call fails because the pnpm root package is src/web/test, not src/web. The node_modules target was never referenced as a dependency, so remove it. Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
|
clang-tidy review says "All clean, LGTM! 👍" |
Add missing field(std::string, const char*) overload to JsonBuilder. Without it, field(getName(), ioTypeToDirection()) resolved to the bool overload (standard pointer-to-bool conversion beats user-defined const char*-to-string), writing true instead of "input"/"output"/"inout". Invert netlistsvg SVG colors in dark mode so strokes and text are visible against the dark background. Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
|
clang-tidy review says "All clean, LGTM! 👍" |
Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
|
clang-tidy review says "All clean, LGTM! 👍" |
tempHtml/tempPng wrote to /tmp/web_test_<label>.{html,png} with no
per-process suffix. Concurrent invocations of the same gtest case
(CI parallel jobs / retries / sharding) raced on the same path:
one process's std::ofstream truncated another's in-flight write,
producing a half-written file that was missing varying substrings
depending on flush timing.
Append getpid() to the filename so each process owns a private path.
Verified by reproducing the race with 32 parallel runs of
SaveReportTest.ContainsInlinedJS (100% failure before, 0% after).
Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
|
clang-tidy review says "All clean, LGTM! 👍" |
|
@oharboe please try again |
38fd259
into
The-OpenROAD-Project:master
|
Very nice! Looks like it is wired up. Of course I want all the features 😆, but I can save a .html page and read the timing reports. The MVP before users will prefer this is histogram support w/buckets one can click on. Once I have that, I'll wire it up in bazel-orfs and we'll roll it out. I expect that we'll need some work on file sizes and pruning. bazel-orfs can implement some default heuristics as to how much to save that the user can tune per project to make suitably sized .html files. 😌 |
Summary
Add a
web_save_reportTcl command that generates a standalone HTML timing report reusing the exact same JS/CSS as the live web viewer.Type of Change
Impact
Architecture:
The static report substitutes the WebSocket server with a cache
layer. WebSocketManager gains a
fromCache(cache)factory thatserves 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:
Verification
./etc/Build.sh).Related Issues
[Link issues here]