Skip to content

Commit 6938d34

Browse files
committed
refactor: use deployment name instead of object as dict keys
Update the scheduler to index the deployment object mappings by `deploy.full_name` instead of the `deploy` object itself. This means that the `Deploy` object does not need to be "hash-able". The `full_name` now takes account of the variant, as without this the deployment objects are not uniquely identifiable from the "full name". From what I can tell the only use of the `full_name` attribute is for logs and for unique IDs, which turns out not to be unique enough. Signed-off-by: James McCorrie <[email protected]>
1 parent a58fd05 commit 6938d34

File tree

6 files changed

+54
-45
lines changed

6 files changed

+54
-45
lines changed

src/dvsim/flow/base.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
from abc import ABC, abstractmethod
1212
from collections.abc import Mapping, Sequence
1313
from pathlib import Path
14-
from typing import ClassVar
14+
from typing import TYPE_CHECKING, ClassVar
1515

1616
import hjson
1717

1818
from dvsim.flow.hjson import set_target_attribute
19-
from dvsim.job.deploy import Deploy
2019
from dvsim.launcher.factory import get_launcher_cls
2120
from dvsim.logging import log
22-
from dvsim.scheduler import Scheduler
21+
from dvsim.scheduler import CompletedJobStatus, Scheduler
2322
from dvsim.utils import (
2423
find_and_substitute_wildcards,
2524
md_results_to_html,
@@ -28,6 +27,11 @@
2827
subst_wildcards,
2928
)
3029

30+
if TYPE_CHECKING:
31+
from dvsim.job.deploy import Deploy
32+
33+
__all__ = ("FlowCfg",)
34+
3135

3236
# Interface class for extensions.
3337
class FlowCfg(ABC):
@@ -399,7 +403,7 @@ def create_deploy_objects(self) -> None:
399403
for item in self.cfgs:
400404
item._create_deploy_objects()
401405

402-
def deploy_objects(self) -> Mapping[Deploy, str]:
406+
def deploy_objects(self) -> Mapping[str, CompletedJobStatus]:
403407
"""Public facing API for deploying all available objects.
404408
405409
Runs each job and returns a map from item to status.
@@ -432,7 +436,7 @@ def deploy_objects(self) -> Mapping[Deploy, str]:
432436
).run()
433437

434438
@abstractmethod
435-
def _gen_results(self, results: Mapping[Deploy, str]) -> str:
439+
def _gen_results(self, results: Mapping[str, CompletedJobStatus]) -> str:
436440
"""Generate flow results.
437441
438442
The function is called after the flow has completed. It collates
@@ -441,13 +445,16 @@ def _gen_results(self, results: Mapping[Deploy, str]) -> str:
441445
prints the full list of failures for debug / triage to the final
442446
report, which is in markdown format.
443447
444-
results should be a dictionary mapping deployed item to result.
448+
Results:
449+
dictionary mapping deployed item names to job status.
450+
445451
"""
446452

447-
def gen_results(self, results: Mapping[Deploy, str]) -> None:
453+
def gen_results(self, results: Mapping[str, CompletedJobStatus]) -> None:
448454
"""Public facing API for _gen_results().
449455
450-
results should be a dictionary mapping deployed item to result.
456+
Args:
457+
results: dictionary mapping deployed item names to job status.
451458
452459
"""
453460
for item in self.cfgs:

src/dvsim/flow/factory.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pathlib
66
import sys
77

8+
from dvsim.flow.base import FlowCfg
89
from dvsim.flow.cdc import CdcCfg
910
from dvsim.flow.formal import FormalCfg
1011
from dvsim.flow.hjson import load_hjson
@@ -105,7 +106,7 @@ def _make_child_cfg(path, args, initial_values):
105106
return cls(path, hjson_data, args, None)
106107

107108

108-
def make_cfg(path, args, proj_root):
109+
def make_cfg(path, args, proj_root) -> FlowCfg:
109110
"""Make a flow config by loading the config file at path.
110111
111112
args is the arguments passed to the dvsim.py tool and proj_root is the top

src/dvsim/flow/sim.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ def create_bucket_report(buckets):
887887

888888
# Append coverage results if coverage was enabled.
889889
if self.cov_report_deploy is not None:
890-
report_status = results[self.cov_report_deploy.qual_name]
890+
report_status = results[self.cov_report_deploy.full_name]
891891
if report_status == "P":
892892
results_str += "\n## Coverage Results\n"
893893
# Link the dashboard page using "cov_report_page" value.

src/dvsim/job/deploy.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(self, sim_cfg: "SimCfg") -> None:
7070
# Cross ref the whole cfg object for ease.
7171
self.sim_cfg = sim_cfg
7272
self.flow = sim_cfg.name
73+
self._variant_suffix = f"_{self.sim_cfg.variant}" if self.sim_cfg.variant else ""
7374

7475
# A list of jobs on which this job depends.
7576
self.dependencies = []
@@ -166,7 +167,7 @@ def _set_attrs(self) -> None:
166167

167168
# Full name disambiguates across multiple cfg being run (example:
168169
# 'aes:default', 'uart:default' builds.
169-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
170+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
170171

171172
# Job name is used to group the job by cfg and target. The scratch path
172173
# directory name is assumed to be uniquified, in case there are more
@@ -574,7 +575,7 @@ def _set_attrs(self) -> None:
574575
self.test = self.name
575576
self.build_mode = self.test_obj.build_mode.name
576577
self.qual_name = self.run_dir_name + "." + str(self.seed)
577-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
578+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
578579
self.job_name += f"_{self.build_mode}"
579580
if self.sim_cfg.cov:
580581
self.output_dirs += [self.cov_db_dir]
@@ -679,7 +680,7 @@ def _define_attrs(self) -> None:
679680
def _set_attrs(self) -> None:
680681
super()._set_attrs()
681682
self.qual_name = self.target
682-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
683+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
683684
self.input_dirs += [self.cov_merge_db_dir]
684685

685686
# Reuse the build_fail_patterns set in the HJson.
@@ -734,7 +735,7 @@ def _define_attrs(self) -> None:
734735
def _set_attrs(self) -> None:
735736
super()._set_attrs()
736737
self.qual_name = self.target
737-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
738+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
738739

739740
# For merging coverage db, the precise output dir is set in the HJson.
740741
self.odir = self.cov_merge_db_dir
@@ -768,7 +769,7 @@ def _define_attrs(self) -> None:
768769
def _set_attrs(self) -> None:
769770
super()._set_attrs()
770771
self.qual_name = self.target
771-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
772+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
772773

773774
# Keep track of coverage results, once the job is finished.
774775
self.cov_total = ""
@@ -819,5 +820,5 @@ def _define_attrs(self) -> None:
819820
def _set_attrs(self) -> None:
820821
super()._set_attrs()
821822
self.qual_name = self.target
822-
self.full_name = self.sim_cfg.name + ":" + self.qual_name
823+
self.full_name = f"{self.sim_cfg.name}{self._variant_suffix}:{self.qual_name}"
823824
self.input_dirs += [self.cov_merge_db_dir]

src/dvsim/scheduler.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,16 @@ def get_next_item(arr: Sequence, index: int) -> tuple[Any, int]:
5656
is already the last item on the list, it loops back to the start, thus
5757
implementing a circular list.
5858
59-
arr is a subscriptable list.
60-
index is the index of the last item returned.
59+
Args:
60+
arr: subscriptable list.
61+
index: index of the last item returned.
62+
63+
Returns:
64+
(item, index) if successful.
65+
66+
Raises:
67+
IndexError if arr is empty.
6168
62-
Returns (item, index) if successful.
63-
Raises IndexError if arr is empty.
6469
"""
6570
index += 1
6671
try:
@@ -95,7 +100,7 @@ def __init__(
95100
# they wait until slots are available for them to be dispatched.
96101
# When all items (in all cfgs) of a target are done, it is removed from
97102
# this dictionary.
98-
self._scheduled: MutableMapping[str, MutableMapping[FlowCfg, MutableSequence[Deploy]]] = {}
103+
self._scheduled: MutableMapping[str, MutableMapping[str, MutableSequence[Deploy]]] = {}
99104
self.add_to_scheduled(items)
100105

101106
# Print status periodically using an external status printer.
@@ -143,20 +148,15 @@ def __init__(
143148
msg = self.msg_fmt.format(0, 0, 0, 0, 0, self._total[target])
144149
self.status_printer.init_target(target=target, msg=msg)
145150

146-
# A map from the Deploy objects tracked by this class to their
151+
# A map from the Deploy object names tracked by this class to their
147152
# current status. This status is 'Q', 'D', 'P', 'F' or 'K',
148153
# corresponding to membership in the dicts above. This is not
149154
# per-target.
150-
self.item_to_status: MutableMapping[Deploy, str] = {}
151-
152-
# TODO: Why is the deployment object asked about which launcher to use when
153-
# the launcher class is explicitly passed. Either each deployment can have it's
154-
# own distinct Launcher class type or all deployments must have the same
155-
# Launcher class? Both can't be true.
155+
self.item_status: MutableMapping[str, str] = {}
156156

157157
# Create the launcher instance for all items.
158158
self._launchers: Mapping[str, Launcher] = {
159-
item.qual_name: launcher_cls(item) for item in self.items
159+
item.full_name: launcher_cls(item) for item in self.items
160160
}
161161

162162
# The chosen launcher class. This allows us to access launcher
@@ -229,11 +229,11 @@ def on_signal(signal_received: int, _: FrameType | None) -> None:
229229

230230
# We got to the end without anything exploding. Return the results.
231231
return {
232-
d.qual_name: CompletedJobStatus(
232+
name: CompletedJobStatus(
233233
status=status,
234-
fail_msg=self._launchers[d.qual_name].fail_msg,
234+
fail_msg=self._launchers[name].fail_msg,
235235
)
236-
for d, status in self.item_to_status.items()
236+
for name, status in self.item_status.items()
237237
}
238238

239239
def add_to_scheduled(self, items: Sequence[Deploy]) -> None:
@@ -271,9 +271,9 @@ def _enqueue_successors(self, item: Deploy | None = None) -> None:
271271
them to _queued.
272272
"""
273273
for next_item in self._get_successors(item):
274-
assert next_item not in self.item_to_status
274+
assert next_item.full_name not in self.item_status
275275
assert next_item not in self._queued[next_item.target]
276-
self.item_to_status[next_item] = "Q"
276+
self.item_status[next_item.full_name] = "Q"
277277
self._queued[next_item.target].append(next_item)
278278
self._unschedule_item(next_item)
279279

@@ -356,11 +356,11 @@ def _ok_to_enqueue(self, item: Deploy) -> bool:
356356
continue
357357

358358
# Has the dep even been enqueued?
359-
if dep not in self.item_to_status:
359+
if dep.full_name not in self.item_status:
360360
return False
361361

362362
# Has the dep completed?
363-
if self.item_to_status[dep] not in ["P", "F", "K"]:
363+
if self.item_status[dep.full_name] not in ["P", "F", "K"]:
364364
return False
365365

366366
return True
@@ -379,7 +379,7 @@ def _ok_to_run(self, item: Deploy) -> bool:
379379
if dep not in self.items:
380380
continue
381381

382-
dep_status = self.item_to_status[dep]
382+
dep_status = self.item_status[dep.full_name]
383383
if dep_status not in ["P", "F", "K"]:
384384
raise ValueError("Status must be one of P, F, or K")
385385

@@ -421,7 +421,7 @@ def _poll(self, hms: str) -> bool:
421421
self._running[target],
422422
self.last_item_polled_idx[target],
423423
)
424-
status = self._launchers[item.qual_name].poll()
424+
status = self._launchers[item.full_name].poll()
425425
level = log.VERBOSE
426426

427427
if status not in ["D", "P", "F", "E", "K"]:
@@ -445,7 +445,7 @@ def _poll(self, hms: str) -> bool:
445445

446446
self._running[target].pop(self.last_item_polled_idx[target])
447447
self.last_item_polled_idx[target] -= 1
448-
self.item_to_status[item] = status
448+
self.item_status[item.full_name] = status
449449

450450
log.log(
451451
level,
@@ -532,7 +532,7 @@ def _dispatch(self, hms: str) -> None:
532532

533533
for item in to_dispatch:
534534
try:
535-
self._launchers[item.qual_name].launch()
535+
self._launchers[item.full_name].launch()
536536

537537
except LauncherError:
538538
log.exception("Error launching %s", item)
@@ -552,7 +552,7 @@ def _dispatch(self, hms: str) -> None:
552552
continue
553553

554554
self._running[target].append(item)
555-
self.item_to_status[item] = "D"
555+
self.item_status[item.full_name] = "D"
556556

557557
def _kill(self) -> None:
558558
"""Kill any running items and cancel any that are waiting."""
@@ -616,7 +616,7 @@ def _cancel_item(self, item: Deploy, *, cancel_successors: bool = True) -> None:
616616
Supplied item may be in _scheduled list or the _queued list. From
617617
either, we move it straight to _killed.
618618
"""
619-
self.item_to_status[item] = "K"
619+
self.item_status[item.full_name] = "K"
620620
self._killed[item.target].add(item)
621621
if item in self._queued[item.target]:
622622
self._queued[item.target].remove(item)
@@ -628,8 +628,8 @@ def _cancel_item(self, item: Deploy, *, cancel_successors: bool = True) -> None:
628628

629629
def _kill_item(self, item: Deploy) -> None:
630630
"""Kill a running item and cancel all of its successors."""
631-
self._launchers[item.qual_name].kill()
632-
self.item_to_status[item] = "K"
631+
self._launchers[item.full_name].kill()
632+
self.item_status[item.full_name] = "K"
633633
self._killed[item.target].add(item)
634634
self._running[item.target].remove(item)
635635
self._cancel_successors(item)

src/dvsim/sim_results.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def __init__(self, items, results) -> None:
8585

8686
def _add_item(self, item, results: Mapping[str, CompletedJobStatus]) -> None:
8787
"""Recursively add a single item to the table of results."""
88-
job_status = results[item.qual_name]
88+
job_status = results[item.full_name]
8989
if job_status.status in ["F", "K"]:
9090
bucket = self._bucketize(job_status.fail_msg.message)
9191
self.buckets[bucket].append(

0 commit comments

Comments
 (0)