diff --git a/decbench/metrics/type_match.py b/decbench/metrics/type_match.py index d39f87d..b118acc 100644 --- a/decbench/metrics/type_match.py +++ b/decbench/metrics/type_match.py @@ -528,8 +528,9 @@ def _get_location(die: Any, dwarfinfo: Any) -> tuple[list[int], bool]: if op.op_name == "DW_OP_fbreg": offsets.append(op.args[0] + 16) - elif loc_attr.form == "DW_FORM_sec_offset": + elif loc_attr.form in ("DW_FORM_sec_offset", "DW_FORM_loclistx"): loclists = dwarfinfo.location_lists() + # pyelftools has resolved a loclistx index to a section offset in value. loclist = loclists.get_location_list_at_offset(loc_attr.value, die=die) for entry in loclist: @@ -841,7 +842,7 @@ class TypeMatchMetric(Metric): display_name = "Type Correctness" description = "Accuracy of variable type recovery vs DWARF ground truth" - cache_version = "18" + cache_version = "20" weight = 1.0 lower_is_better = False diff --git a/docs/metrics.md b/docs/metrics.md index fc97b88..7bac609 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -118,8 +118,8 @@ Ground truth is the function's OWN parameters and locals: the variables of an inlined callee (`DW_TAG_inlined_subroutine`, at `-O2` mostly the parameters of glibc's `_FORTIFY_SOURCE` wrappers such as `printf`'s `__fmt`) are not scored, since the compiled function usually has no storage for them. -Current `cache_version="18"`, bumped for scoped syntax-aware declaration parsing, -aggregate/qualifier normalization, and DWARF restrict-type passthrough. +Current `cache_version="20"`, bumped to include DWARF 5 indexed location lists +(`DW_FORM_loclistx`) when extracting ground-truth variables. The per-function key covers source and decompiler address evidence, argument/stack anchors, recovered types, source selection diagnostics, and the matching thresholds. diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 7aafa35..b337269 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -432,6 +432,34 @@ def __init__(self, index: int, *, entry: bool, exit: bool): class TestTypeMatchMetric: """Tests for the type match metric.""" + @pytest.mark.parametrize("form", ["DW_FORM_sec_offset", "DW_FORM_loclistx"]) + @pytest.mark.parametrize( + ("expression", "expected"), + [([0x50], ([], True)), ([0x91, 0x78], ([8], True)), ([], ([], False))], + ) + def test_location_lists_use_decoded_offset( + self, form: str, expression: list[int], expected: tuple[list[int], bool] + ) -> None: + from types import SimpleNamespace + + from elftools.dwarf.structs import DWARFStructs + + from decbench.metrics.type_match import _get_location + + attribute = SimpleNamespace(form=form, value=0x38, raw_value=2) + die = SimpleNamespace(attributes={"DW_AT_location": attribute}) + + def read_list(offset: int, *, die: SimpleNamespace) -> list[SimpleNamespace]: + assert offset == 0x38 + assert die.attributes["DW_AT_location"] is attribute + return [SimpleNamespace(loc_expr=expression)] + + dwarf = SimpleNamespace( + structs=DWARFStructs(little_endian=True, dwarf_format=32, address_size=8), + location_lists=lambda: SimpleNamespace(get_location_list_at_offset=read_list), + ) + assert _get_location(die, dwarf) == expected + def test_type_match_registration(self) -> None: MetricRegistry.clear() from decbench.metrics.type_match import TypeMatchMetric