Files
TheRock/compiler/amd-llvm/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Nodes.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

205 lines
7.0 KiB
Python

# DExTer : Debugging Experience Tester
# ~~~~~~ ~ ~~ ~ ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""This file defines all of the Nodes used to create Dexter scripts. All Nodes must be registered with the yaml
constructor/representer in `setup_yaml_parser` before loading or printing any script.
"""
import abc
from dataclasses import dataclass
from typing import Any, Dict, Optional, Union
import yaml
from dex.dextIR.ValueIR import ValueIR
from dex.utils.Exceptions import Error
def setup_yaml_parser(loader):
reg_classes = [
Where,
Value,
DexRange,
]
for c in reg_classes:
c.register_yaml(loader)
class DexterNodeError(Error):
"""Class representing errors with Dexter node parsing."""
def __init__(self, node, msg):
super(DexterNodeError, self).__init__(msg)
self.msg = msg
self.node = node
def __str__(self):
return f"Error with node: {self.node}: {self.msg}"
###################
## Structural Nodes: These are used as keys in the Script, and collectively define Dexter's actions when running a test:
## how it steps and navigates through the debuggee program, and what information it collects from the
## debugger.
class Where:
"""One or more instances of this class define a range of steps in a debugging session. Any expects in the script
within scope of a "Where" will only be evaluated for the steps where the Where applies.
"""
def __init__(self, attributes: dict):
self.file: Optional[str] = attributes.pop("file", None)
self.function: Union[list[str], str, None] = attributes.pop("function", None)
self.lines: Union[int, DexRange, None] = attributes.pop("lines", None)
self.after_hit_count: Optional[int] = attributes.pop("after_hit_count", None)
self.for_hit_count: Optional[int] = attributes.pop("for_hit_count", None)
self.conditions: dict = attributes.pop("conditions", None)
if attributes:
raise DexterNodeError(
self, f"unexpected attributes {', '.join(attributes)}"
)
if (
not self.function
and not self.lines
and (self.for_hit_count or self.after_hit_count)
):
raise DexterNodeError(
self, "can't check hit counts without an explicit lines or function arg"
)
def __repr__(self):
elts = [
f"{name}={value}"
for name, value in self.get_attrs().items()
if value is not None
]
return f"Where(" + ", ".join(elts) + ")"
def get_attrs(self) -> Dict[str, Any]:
return {
"file": self.file,
"function": self.function,
"lines": self.lines,
"for_hit_count": self.for_hit_count,
"after_hit_count": self.after_hit_count,
"conditions": self.conditions,
}
@staticmethod
def constructor(loader: yaml.Loader, node):
return Where(loader.construct_mapping(node))
@staticmethod
def representer(dumper: yaml.Dumper, data: "Where"):
mapping = {
name: value for name, value in data.get_attrs().items() if value is not None
}
return dumper.represent_mapping("!where", mapping, flow_style=True)
@staticmethod
def register_yaml(loader):
yaml.add_constructor("!where", Where.constructor, loader)
yaml.add_representer(Where, Where.representer)
def get_lines(self) -> range:
"""Returns the range of line numbers that this Where references, returning an empty range if this Where does not
refer to any lines."""
if not self.lines:
return range(-1)
if isinstance(self.lines, int):
return range(self.lines, self.lines + 1)
assert isinstance(
self.lines, DexRange
), f"Invalid type for lines: {self.lines}: ({type(self.lines)})"
return self.lines.to_range()
###################
## Expect Nodes: These nodes define the expected outputs from the debugger - they are the only nodes that produce
## metrics, and map to an expected value in the script.
class Expect:
"""An expectation of some debugger state that will be compared to actual observed debugger state and generate one
or more metrics as a measurement of the difference.
Expects are largely evaluated independently, but may influence each other through the evaluation context.
"""
@staticmethod
def get_variable_result(value: ValueIR) -> Optional[str]:
"""For Expects that extract actual results from ValueIR, this method returns that result from the given value,
excluding any subvalues (i.e. struct members), or None if there is no valid result for this ValueIR.
"""
@abc.abstractmethod
def get_watched_expr(self) -> str:
"""Returns the list of expressions that this Expect wants to evaluate."""
class Value(Expect):
def __init__(self, variable_name: str):
self.variable_name = variable_name
self.actual_values = None
@staticmethod
def get_variable_result(value: ValueIR) -> Optional[str]:
if value.could_evaluate and not (
value.is_irretrievable or value.is_optimized_away
):
return value.value
return None
def get_watched_expr(self) -> str:
return self.variable_name
def __repr__(self):
return f"Value({self.variable_name})"
@staticmethod
def constructor(loader: yaml.Loader, node):
return Value(loader.construct_scalar(node))
@staticmethod
def representer(dumper, data):
return dumper.represent_scalar("!value", data.variable_name)
@staticmethod
def register_yaml(loader):
yaml.add_constructor("!value", Value.constructor, loader)
yaml.add_representer(Value, Value.representer)
##############
## Utility Nodes: Can be used anywhere in a script as a form of syntactic sugar.
@dataclass(frozen=True)
class DexRange:
start: int
stop: int
def __repr__(self) -> str:
return f"[{self.start} - {self.stop}]"
# We use an inclusive range in Dexter scripts, while python ranges are exclusive.
def to_range(self) -> range:
return range(self.start, self.stop + 1)
@staticmethod
def constructor(loader: yaml.Loader, node):
range_seq = loader.construct_sequence(node)
if len(range_seq) != 2 or not all(isinstance(elt, int) for elt in range_seq):
raise DexterNodeError(node, "range must have exactly 2 int elements")
return DexRange(range_seq[0], range_seq[1])
@staticmethod
def representer(dumper, data: "DexRange"):
return dumper.represent_sequence("!range", [data.start, data.stop])
@staticmethod
def register_yaml(loader):
yaml.add_constructor("!range", DexRange.constructor, loader)
yaml.add_representer(DexRange, DexRange.representer)