From 6fb1cc85dac941aabc20cbd59e33a475c2bf57a9 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 21 May 2026 14:03:05 +0100 Subject: [PATCH 1/4] refactor: remove re-exports of instrumentation imports These aren't particularly necessary, are currently causing issues with the click CLI autocomplete times due to the modules being loaded each time, and will potentially long-term cause circular import issues. Let's just use the full paths for imports instead here. Signed-off-by: Alex Jones --- src/dvsim/cli/run.py | 3 +- src/dvsim/flow/base.py | 2 +- src/dvsim/instrumentation/__init__.py | 33 ------------------- src/dvsim/instrumentation/report/__init__.py | 14 -------- src/dvsim/instrumentation/report/base.py | 3 +- src/dvsim/instrumentation/report/breakdown.py | 3 +- src/dvsim/instrumentation/report/longest.py | 2 +- src/dvsim/instrumentation/report/timelines.py | 3 +- src/dvsim/instrumentation/report/usage.py | 7 ++-- src/dvsim/instrumentation/runtime.py | 7 ++-- src/dvsim/scheduler/runner.py | 2 +- 11 files changed, 15 insertions(+), 64 deletions(-) diff --git a/src/dvsim/cli/run.py b/src/dvsim/cli/run.py index 3398555e..73dee12c 100644 --- a/src/dvsim/cli/run.py +++ b/src/dvsim/cli/run.py @@ -32,7 +32,8 @@ from pathlib import Path from dvsim.flow.factory import make_cfg -from dvsim.instrumentation import InstrumentationFactory, set_instrumentation +from dvsim.instrumentation.factory import InstrumentationFactory +from dvsim.instrumentation.runtime import set_instrumentation from dvsim.job.deploy import RunTest from dvsim.launcher.base import Launcher from dvsim.launcher.lsf import LsfLauncher diff --git a/src/dvsim/flow/base.py b/src/dvsim/flow/base.py index c11ac747..4345a31b 100644 --- a/src/dvsim/flow/base.py +++ b/src/dvsim/flow/base.py @@ -16,7 +16,7 @@ import hjson -from dvsim import instrumentation +import dvsim.instrumentation.runtime as instrumentation from dvsim.flow.hjson import set_target_attribute from dvsim.job.data import CompletedJobStatus, JobSpec, WorkspaceConfig from dvsim.job.status import JobStatus diff --git a/src/dvsim/instrumentation/__init__.py b/src/dvsim/instrumentation/__init__.py index 7f6fd9e0..cdc4d087 100644 --- a/src/dvsim/instrumentation/__init__.py +++ b/src/dvsim/instrumentation/__init__.py @@ -3,36 +3,3 @@ # SPDX-License-Identifier: Apache-2.0 """DVSim Scheduler Instrumentation.""" - -from dvsim.instrumentation.base import InstrumentationAggregator, SchedulerInstrumentation -from dvsim.instrumentation.factory import InstrumentationFactory -from dvsim.instrumentation.records import ( - InstrumentationMetrics, - InstrumentationResults, - JobMetrics, - SchedulerMetrics, -) -from dvsim.instrumentation.runtime import ( - flush, - gen_html_report, - get, - get_report, - set_instrumentation, - set_report_path, -) - -__all__ = ( - "InstrumentationAggregator", - "InstrumentationFactory", - "InstrumentationMetrics", - "InstrumentationResults", - "JobMetrics", - "SchedulerInstrumentation", - "SchedulerMetrics", - "flush", - "gen_html_report", - "get", - "get_report", - "set_instrumentation", - "set_report_path", -) diff --git a/src/dvsim/instrumentation/report/__init__.py b/src/dvsim/instrumentation/report/__init__.py index 624c1e31..435b8f45 100644 --- a/src/dvsim/instrumentation/report/__init__.py +++ b/src/dvsim/instrumentation/report/__init__.py @@ -3,17 +3,3 @@ # SPDX-License-Identifier: Apache-2.0 """DVSim Scheduler Instrumentation report.""" - -from dvsim.instrumentation.report.base import ( - InstrumentationVisualizer, - RenderProfile, - render_html_report, -) -from dvsim.instrumentation.report.registry import ReportVisualizationRegistry - -__all__ = ( - "InstrumentationVisualizer", - "RenderProfile", - "ReportVisualizationRegistry", - "render_html_report", -) diff --git a/src/dvsim/instrumentation/report/base.py b/src/dvsim/instrumentation/report/base.py index 36d1d52b..33905a8f 100644 --- a/src/dvsim/instrumentation/report/base.py +++ b/src/dvsim/instrumentation/report/base.py @@ -14,8 +14,7 @@ import plotly.offline from typing_extensions import Self -from dvsim.instrumentation import InstrumentationResults -from dvsim.instrumentation.records import JobInstrumentationMetadata +from dvsim.instrumentation.records import InstrumentationResults, JobInstrumentationMetadata from dvsim.logging import log from dvsim.report.artifacts import ReportArtifacts, render_static_content from dvsim.templates.render import render_template diff --git a/src/dvsim/instrumentation/report/breakdown.py b/src/dvsim/instrumentation/report/breakdown.py index f1a7dd94..bbc858f3 100644 --- a/src/dvsim/instrumentation/report/breakdown.py +++ b/src/dvsim/instrumentation/report/breakdown.py @@ -10,8 +10,7 @@ import plotly.graph_objects as go from plotly.subplots import make_subplots -from dvsim.instrumentation import InstrumentationResults -from dvsim.instrumentation.records import JobInstrumentationResults +from dvsim.instrumentation.records import InstrumentationResults, JobInstrumentationResults from dvsim.instrumentation.report.base import ( DEFAULT_VISUALIZATION_HEIGHT_PX, PLOTLY_TIMING_AXIS_CONFIG, diff --git a/src/dvsim/instrumentation/report/longest.py b/src/dvsim/instrumentation/report/longest.py index 051cb5e7..35303bc0 100644 --- a/src/dvsim/instrumentation/report/longest.py +++ b/src/dvsim/instrumentation/report/longest.py @@ -12,9 +12,9 @@ import plotly.graph_objects as go from typing_extensions import Self -from dvsim.instrumentation import InstrumentationResults from dvsim.instrumentation.records import ( ConcreteJobTimingMetrics, + InstrumentationResults, JobInstrumentationMetadata, JobInstrumentationResults, ) diff --git a/src/dvsim/instrumentation/report/timelines.py b/src/dvsim/instrumentation/report/timelines.py index db21ef5f..c13fde0c 100644 --- a/src/dvsim/instrumentation/report/timelines.py +++ b/src/dvsim/instrumentation/report/timelines.py @@ -24,8 +24,7 @@ from matplotlib.ticker import MaxNLocator from typing_extensions import Self, override -from dvsim.instrumentation import InstrumentationResults -from dvsim.instrumentation.records import ConcreteJobTimingMetrics +from dvsim.instrumentation.records import ConcreteJobTimingMetrics, InstrumentationResults from dvsim.instrumentation.report.base import ( DEFAULT_VISUALIZATION_HEIGHT_PX, PLOTLY_TIMING_AXIS_CONFIG, diff --git a/src/dvsim/instrumentation/report/usage.py b/src/dvsim/instrumentation/report/usage.py index 6b54ee2f..0e5f23b3 100644 --- a/src/dvsim/instrumentation/report/usage.py +++ b/src/dvsim/instrumentation/report/usage.py @@ -10,8 +10,11 @@ import plotly.graph_objects as go from plotly.graph_objs import Figure -from dvsim.instrumentation import InstrumentationResults -from dvsim.instrumentation.records import ConcreteJobTimingMetrics, JobInstrumentationResults +from dvsim.instrumentation.records import ( + ConcreteJobTimingMetrics, + InstrumentationResults, + JobInstrumentationResults, +) from dvsim.instrumentation.report.base import ( DEFAULT_VISUALIZATION_HEIGHT_PX, PLOTLY_TIMING_AXIS_CONFIG, diff --git a/src/dvsim/instrumentation/runtime.py b/src/dvsim/instrumentation/runtime.py index 2ee25ef6..6a03fbbd 100644 --- a/src/dvsim/instrumentation/runtime.py +++ b/src/dvsim/instrumentation/runtime.py @@ -7,11 +7,8 @@ from pathlib import Path from dvsim.instrumentation.base import InstrumentationAggregator, InstrumentationResults -from dvsim.instrumentation.report import ( - RenderProfile, - ReportVisualizationRegistry, - render_html_report, -) +from dvsim.instrumentation.report.base import RenderProfile, render_html_report +from dvsim.instrumentation.report.registry import ReportVisualizationRegistry from dvsim.logging import log __all__ = ( diff --git a/src/dvsim/scheduler/runner.py b/src/dvsim/scheduler/runner.py index 587f5c8e..0244fff3 100644 --- a/src/dvsim/scheduler/runner.py +++ b/src/dvsim/scheduler/runner.py @@ -6,7 +6,7 @@ from collections.abc import Iterable -from dvsim import instrumentation +import dvsim.instrumentation.runtime as instrumentation from dvsim.job.data import CompletedJobStatus, JobSpec from dvsim.runtime.backend import RuntimeBackend from dvsim.runtime.fake import FakePolicy, FakeRuntimeBackend From d50226bf9bbb1d0f4b517ec8e57baefa06806783 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Thu, 21 May 2026 14:10:27 +0100 Subject: [PATCH 2/4] refactor: move instrumentation RenderProfile enum to its own module This keeps this as a nice light-weight import that can be used by the click CLI, also helping to keep potentially user-facing data boundaries isolated. Signed-off-by: Alex Jones --- src/dvsim/instrumentation/report/base.py | 10 +--------- src/dvsim/instrumentation/report/longest.py | 2 +- src/dvsim/instrumentation/report/profile.py | 15 +++++++++++++++ src/dvsim/instrumentation/report/registry.py | 3 ++- src/dvsim/instrumentation/report/timelines.py | 2 +- src/dvsim/instrumentation/runtime.py | 3 ++- 6 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 src/dvsim/instrumentation/report/profile.py diff --git a/src/dvsim/instrumentation/report/base.py b/src/dvsim/instrumentation/report/base.py index 33905a8f..f455f552 100644 --- a/src/dvsim/instrumentation/report/base.py +++ b/src/dvsim/instrumentation/report/base.py @@ -5,7 +5,6 @@ """DVSim scheduler instrumentation reporting & visualizations.""" from collections.abc import Iterable, Mapping, Sequence -from enum import Enum from pathlib import Path from typing import Any, Protocol, TypeVar @@ -15,6 +14,7 @@ from typing_extensions import Self from dvsim.instrumentation.records import InstrumentationResults, JobInstrumentationMetadata +from dvsim.instrumentation.report.profile import RenderProfile from dvsim.logging import log from dvsim.report.artifacts import ReportArtifacts, render_static_content from dvsim.templates.render import render_template @@ -45,14 +45,6 @@ } -class RenderProfile(Enum): - """Levels of visualization rendering detail, which impact report size & responsiveness.""" - - NORMAL = "normal" - HIGH = "high" - FULL = "full" - - class InstrumentationVisualizer(Protocol): """Builder & renderer for HTML instrumentation visualizations.""" diff --git a/src/dvsim/instrumentation/report/longest.py b/src/dvsim/instrumentation/report/longest.py index 35303bc0..926fc7e7 100644 --- a/src/dvsim/instrumentation/report/longest.py +++ b/src/dvsim/instrumentation/report/longest.py @@ -22,11 +22,11 @@ DEFAULT_VISUALIZATION_HEIGHT_PX, PLOTLY_TIMING_AXIS_CONFIG, InstrumentationVisualizer, - RenderProfile, get_default_color_map, make_job_metadata_hover, render_plotly_figure, ) +from dvsim.instrumentation.report.profile import RenderProfile from dvsim.job.status import JobStatus from dvsim.logging import log from dvsim.utils import format_time_metric, ordinal_suffix diff --git a/src/dvsim/instrumentation/report/profile.py b/src/dvsim/instrumentation/report/profile.py new file mode 100644 index 00000000..3039696e --- /dev/null +++ b/src/dvsim/instrumentation/report/profile.py @@ -0,0 +1,15 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +"""DVSim scheduler instrumentation reporting rendering profiles/levels.""" + +from enum import Enum + + +class RenderProfile(Enum): + """Levels of visualization rendering detail, which impact report size & responsiveness.""" + + NORMAL = "normal" + HIGH = "high" + FULL = "full" diff --git a/src/dvsim/instrumentation/report/registry.py b/src/dvsim/instrumentation/report/registry.py index d02a8d0f..df43a336 100644 --- a/src/dvsim/instrumentation/report/registry.py +++ b/src/dvsim/instrumentation/report/registry.py @@ -6,7 +6,7 @@ from typing import ClassVar -from dvsim.instrumentation.report.base import InstrumentationVisualizer, RenderProfile +from dvsim.instrumentation.report.base import InstrumentationVisualizer from dvsim.instrumentation.report.breakdown import BlockVariantBreakdown, ToolBreakdown from dvsim.instrumentation.report.longest import ( LongestByBlockChart, @@ -15,6 +15,7 @@ LongestTestsByBlockChart, LongestTestsByToolChart, ) +from dvsim.instrumentation.report.profile import RenderProfile from dvsim.instrumentation.report.timelines import ParallelismChart, TimelineBarChart from dvsim.instrumentation.report.usage import ToolUsageLineGraph diff --git a/src/dvsim/instrumentation/report/timelines.py b/src/dvsim/instrumentation/report/timelines.py index c13fde0c..9683375e 100644 --- a/src/dvsim/instrumentation/report/timelines.py +++ b/src/dvsim/instrumentation/report/timelines.py @@ -29,11 +29,11 @@ DEFAULT_VISUALIZATION_HEIGHT_PX, PLOTLY_TIMING_AXIS_CONFIG, InstrumentationVisualizer, - RenderProfile, get_default_color_map, make_job_metadata_hover, render_plotly_figure, ) +from dvsim.instrumentation.report.profile import RenderProfile from dvsim.logging import log from dvsim.utils import format_time_as_hms as format_time from dvsim.utils import format_time_metric diff --git a/src/dvsim/instrumentation/runtime.py b/src/dvsim/instrumentation/runtime.py index 6a03fbbd..baf532a7 100644 --- a/src/dvsim/instrumentation/runtime.py +++ b/src/dvsim/instrumentation/runtime.py @@ -7,7 +7,8 @@ from pathlib import Path from dvsim.instrumentation.base import InstrumentationAggregator, InstrumentationResults -from dvsim.instrumentation.report.base import RenderProfile, render_html_report +from dvsim.instrumentation.report.base import render_html_report +from dvsim.instrumentation.report.profile import RenderProfile from dvsim.instrumentation.report.registry import ReportVisualizationRegistry from dvsim.logging import log From 1ca03d21665702643dba925cfa607a252597858c Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 20 May 2026 23:11:15 +0100 Subject: [PATCH 3/4] fix: ruff error on click dvsim-admin CLI docstring There was an existing ruff D401 error here because ruff wants the docstring to be in an imperative mood, but the click CLI uses the docstring to determine the actual documentation provided in the `dvsim-admin --help` menu - so we don't want ruff dictating the text here. Tell ruff to ignore this with an appropriate noqa. Signed-off-by: Alex Jones --- src/dvsim/cli/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvsim/cli/admin.py b/src/dvsim/cli/admin.py index 754588fe..9fcfe292 100644 --- a/src/dvsim/cli/admin.py +++ b/src/dvsim/cli/admin.py @@ -62,7 +62,7 @@ def dashboard_gen(json_path: Path, output_dir: Path, base_url: str | None) -> No @cli.group() def report() -> None: - """Reporting helper commands.""" + """Reporting helper commands.""" # noqa: D401 @report.command("gen") From 50060121c20cc0f389ae31b621af0252477fec99 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Wed, 13 May 2026 14:07:04 +0100 Subject: [PATCH 4/4] feat: add instrumentation report generation to the dvsim-admin CLI This will allow instrumentation reports to be re-generated from previously saved `metrics.json` files after runs are completed. Importantly, it also provides an interface for specifying a custom rendering profile. This allows us to reduce the level of detail in regular DVSim runs, to minimize the size and increase the performance of the generated instrumentation reports - whilst retaining the ability to browse fully-detailed reports using `dvsim-admin report instrumentation` with e.g. `--profile=high` or `--profile=full` as needed. Signed-off-by: Alex Jones --- src/dvsim/cli/admin.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/dvsim/cli/admin.py b/src/dvsim/cli/admin.py index 9fcfe292..93279f6d 100644 --- a/src/dvsim/cli/admin.py +++ b/src/dvsim/cli/admin.py @@ -10,6 +10,8 @@ import click +from dvsim.instrumentation.report.profile import RenderProfile + @click.group() @click.version_option(version("dvsim")) @@ -91,5 +93,28 @@ def report_gen(json_path: Path, output_dir: Path) -> None: ) +@report.command( + "instrumentation", short_help="Generate an instrumentation report from existing metrics." +) +@click.argument( + "json_path", + type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path), +) +@click.argument("output_dir", type=click.Path(file_okay=False, dir_okay=True, path_type=Path)) +@click.option( + "--profile", + type=click.Choice([e.value for e in RenderProfile], case_sensitive=False), + help="Set the rendering profile to control the detail vs. report optimization", +) +def instrumentation_report_gen(json_path: Path, output_dir: Path, profile: str | None) -> None: + """Generate an instrumentation report from an existing metrics JSON.""" + from dvsim.instrumentation.records import InstrumentationResults # noqa: PLC0415 + from dvsim.instrumentation.runtime import gen_html_report # noqa: PLC0415 + + render_profile = None if profile is None else RenderProfile(profile) + results = InstrumentationResults.model_validate_json(json_path.read_text(encoding="utf-8")) + gen_html_report(results, profile=render_profile, outdir=output_dir) + + if __name__ == "__main__": sys.exit(cli())