https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/202799
>From a40271fe2f46912f757cafcf9fb95724e3dc2b9f Mon Sep 17 00:00:00 2001 From: Stephen Tozer <[email protected]> Date: Tue, 9 Jun 2026 15:40:46 +0100 Subject: [PATCH 1/2] [Dexter] Add ability to rewrite scripts to fill-in unknown values This patch adds a feature to Dexter that allows scripts to be passed to Dexter with missing expected values (`null` values in YAML), which Dexter will attempt to "fill-in" with expected values that match the debugger's actual output. The result is written to a file with the same name as the original test file, in the directory given by --results-directory if one is present; all content outside of the Dexter script itself is preserved exactly as-is. --- .../dexter/dex/evaluation/ExpectWriter.py | 201 ++++++++++++++++++ .../dexter/dex/test_script/Script.py | 26 +++ .../dexter/dex/tools/ToolBase.py | 3 +- .../dexter/dex/tools/test/Tool.py | 40 +++- .../Inputs/rewrite_expect_list_expected.cpp | 128 +++++++++++ .../Inputs/rewrite_expects_expected.cpp | 51 +++++ .../rewrite_multiple_scripts_expected.cpp | 46 ++++ .../scripts/rewriting/Inputs/simple_prog.cpp | 7 + .../rewriting/Inputs/whole_file_test.dex | 2 + .../Inputs/whole_file_test_expected.dex | 6 + .../scripts/rewriting/rewrite_expect_list.cpp | 42 ++++ .../scripts/rewriting/rewrite_expects.cpp | 52 +++++ .../rewriting/rewrite_multiple_scripts.cpp | 46 ++++ .../scripts/rewriting/whole_file.test | 22 ++ 14 files changed, 665 insertions(+), 7 deletions(-) create mode 100644 cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expect_list_expected.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expects_expected.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test.dex create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test_expected.dex create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expect_list.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expects.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/whole_file.test diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py new file mode 100644 index 0000000000000..b943aadc9abfa --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/ExpectWriter.py @@ -0,0 +1,201 @@ +# 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 +"""Utilities for using debugger output to generate expected values that match that output.""" + +from collections import Counter, OrderedDict, defaultdict +from copy import deepcopy +from enum import Enum, IntEnum +from typing import Any, Dict, List, Optional, Set, Tuple, Union + +from dex.dextIR import DextIR, StepIR, ValueIR +from dex.evaluation.StateMatch import get_active_where_matches +from dex.test_script.Nodes import Expect, Then, Value, Where +from dex.test_script.Script import DexterScript, Scope +from dex.tools.Main import Context + + +class ExpectedValueWriter: + """Given a ValueIR for an Expect, generates a complete expected value that matches that value if one can be + provided.""" + + def __init__(self, expect: Expect, value: ValueIR): + self.expect = expect + self.root_value = value + self.expected_value = expect.get_variable_result(value) + + +def unique_expected_values(elements: List[ExpectedValueWriter]): + """Given a list of ExpectedValueWriters, and returns either a list containing the unique set of non-None expected + values, or a single item if there is only one non-duplicated expected value in the list, or None if there are no + valid expected values.""" + + unique_set = set() + result = [] + for element in elements: + expected_value = element.expected_value + if expected_value is None: + continue + if expected_value not in unique_set: + unique_set.add(expected_value) + result.append(expected_value) + if not result: + return None + if len(result) == 1: + return result[0] + return result + + +class StepExpectWriter: + """Processes all active, unknown expects at a given debugger step and produces ExpectedValueWriter results for + each.""" + + def __init__(self, step: StepIR, script: DexterScript): + self.step = step + self.script = script + self.state_match = get_active_where_matches(script, step) + active_expects = { + expect + for where_match in self.state_match.values() + for expect in where_match.active_expects + } + self.expect_matches: Dict[Expect, ExpectedValueWriter] = {} + + def add_expected_values(expect: Expect, expected_value: Any, scope: Scope): + assert isinstance(expect, Value), "Non-Value expects currently unsupported" + if expect in active_expects and expected_value is None: + self.expect_matches[expect] = ExpectedValueWriter( + expect, step.watches[expect.get_watched_expr()] + ) + + script.visit_script(visit_expect=add_expected_values) + + +class ScriptExpectWriter: + """Given the full output from a debugger run and a script with missing expected values, returns a script with + filled-in expected values that match the debugger output.""" + + def __init__(self, context: Context, dext_ir: DextIR): + self.context = context + self.dext_ir = dext_ir + self.unknown_expect_rewrites: Dict[ + Expect, List[Tuple[int, ExpectedValueWriter]] + ] = {} + self.new_script: Optional[DexterScript] = None + self.new_expected_values: Dict[Expect, Any] = {} + self.missing_expect_rewrites: List[Expect] = [] + + def collect_unknown_expects(expect: Expect, expected_value: Any, scope: Scope): + assert isinstance(expect, Value), "Non-Value expects currently unsupported" + if expected_value is None: + self.unknown_expect_rewrites[expect] = [] + + script = dext_ir.script + assert ( + script is not None + ), "Cannot use ScriptExpectWriter on a non-script Dexter test." + script.visit_script(visit_expect=collect_unknown_expects) + + # If there are no expects to update, then there is no rewriting to be done - exit early. + if not self.unknown_expect_rewrites: + return + + self.step_writers = [StepExpectWriter(step, script) for step in dext_ir.steps] + for step_writer in self.step_writers: + step_idx = step_writer.step.step_index + for expect, expected_value_writer in step_writer.expect_matches.items(): + self.unknown_expect_rewrites[expect].append( + (step_idx, expected_value_writer) + ) + + self.new_expected_values = { + expect: expected_values + for expect, expect_writers in self.unknown_expect_rewrites.items() + if ( + expected_values := unique_expected_values( + [writer for idx, writer in expect_writers] + ) + ) + is not None + } + self.new_script = rewrite_script(script, self.new_expected_values) + self.missing_expect_rewrites = [ + expect + for expect in self.unknown_expect_rewrites + if expect not in self.new_expected_values + ] + + @property + def num_successful_rewrites(self): + return len(self.new_expected_values) + + @property + def num_unsuccessful_rewrites(self): + return len(self.missing_expect_rewrites) + + +def rewrite_script( + script: DexterScript, add_expected_values: Dict[Expect, Any] +) -> DexterScript: + """Given a set of updates to apply to a provided script, returns a copy of the script_obj with the updates + applied. + Does not deep copy, meaning the new script contains the same node objects as the old script; this is safe as we do + not modify these objects.""" + # First build up a map describing the children of every node in the script, adding add_expected_values to the + # required expect nodes. + new_node_child_map = {} + + def replace_where(where: Where, scope: Scope): + if scope.where: + scope_where_children = new_node_child_map.setdefault(scope.where, []) + assert isinstance( + scope_where_children, list + ), f"Unexpected child for !where node: {scope_where_children}" + scope_where_children.append(where) + + def replace_then(then: Then, scope: Scope): + assert ( + scope.where not in new_node_child_map + ), "!then must be the sole child of a state node." + new_node_child_map[scope.where] = then + + def replace_expect(expect: Expect, expected_value, scope: Scope): + new_expected_value = add_expected_values.get(expect) or expected_value + new_node_child_map[expect] = new_expected_value + scope_where_children = new_node_child_map.setdefault(scope.where, []) + assert isinstance( + scope_where_children, list + ), f"Unexpected child for state node {scope.where}: {scope_where_children}" + scope_where_children.append(expect) + + script.visit_script( + visit_where=replace_where, visit_expect=replace_expect, visit_then=replace_then + ) + + # Now rebuild the script object using the two maps. + def build_subscript(node): + """Returns the subset of the script object whose parent is the given node.""" + assert isinstance( + node, (Expect, Where) + ), f"Unexpected script parent node: {node}" + if isinstance(node, Expect): + return new_node_child_map[node] + node_children = new_node_child_map[node] + if isinstance(node_children, Then): + return node_children + assert isinstance( + node_children, List + ), f"Unexpected child for state node {node}: {node_children}" + return {child: build_subscript(child) for child in node_children} + + new_script_obj = {node: build_subscript(node) for node in script.script_obj} + return DexterScript( + script.context, + new_script_obj, + script.root_scope, + script.base_dir, + script.load_context, + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Script.py b/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Script.py index 26a38a604dbe5..50a5cb48a200e 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Script.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/test_script/Script.py @@ -139,6 +139,16 @@ def get_known_file_for_where(self, where: Where) -> Optional[str]: return next_scope.file +class ScriptLoadContext: + """Contains information about the context that the script was loaded from.""" + + def __init__(self, file: str, lines: List[str], start_line: int, stop_line: int): + self.file = file + self.lines = lines + self.start_line = start_line + self.stop_line = stop_line + + class DexterScript: def __init__( self, @@ -146,10 +156,12 @@ def __init__( script_obj, scope: Scope, source_root_dir: Optional[str], + load_context: ScriptLoadContext, ): self.context = context self.script_obj = script_obj self.root_scope = scope + self.load_context = load_context self.label_dict = LabelDict() assert scope.file is not None self.base_dir = ( @@ -271,6 +283,7 @@ def get_script(context, file, loader, source_root_dir: Optional[str]) -> DexterS try_load_yaml("\n".join(lines), loader), root_scope, source_root_dir, + ScriptLoadContext(file, lines, start_line=0, stop_line=len(lines)), ) except (Error, yaml.YAMLError) as e: raise Error(f"File '{file}' was not a valid Dexter script:\n{e}") @@ -293,6 +306,7 @@ def get_script(context, file, loader, source_root_dir: Optional[str]) -> DexterS ), root_scope, source_root_dir, + ScriptLoadContext(file, lines, start_line, stop_line), ) except (Error, yaml.YAMLError) as e: attempted_scripts.append((start_line, e)) @@ -330,3 +344,15 @@ def check_explicit_files(where: Where, _: Scope): script.visit_script(visit_where=check_explicit_files) return script, source_files + + +def write_dexter_script_file(script: DexterScript) -> str: + load_context = script.load_context + script_lines = script.dump().splitlines(True) + write_lines = ( + load_context.lines[: load_context.start_line] + + script_lines + + ["...\n"] + + load_context.lines[load_context.stop_line :] + ) + return "".join(write_lines) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py index 4b09c134a1b6e..d54dd3924e407 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py @@ -11,6 +11,7 @@ import tempfile from dex import __version__ +from dex.tools.Main import Context from dex.utils import ExtArgParse from dex.utils import PrettyOutput from dex.utils.ReturnCode import ReturnCode @@ -18,7 +19,7 @@ class ToolBase(object, metaclass=abc.ABCMeta): def __init__(self, context): - self.context = context + self.context: Context = context self.parser = None @abc.abstractproperty diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py index 0c028773ec56c..ed14a95aece75 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py @@ -24,8 +24,13 @@ ) from dex.dextIR.DextIR import DextIR from dex.evaluation import DebuggerRunMatch +from dex.evaluation.ExpectWriter import ScriptExpectWriter from dex.heuristic import Heuristic -from dex.test_script.Script import get_dexter_script +from dex.test_script.Script import ( + DexterScript, + get_dexter_script, + write_dexter_script_file, +) from dex.tools import TestToolBase from dex.utils.Exceptions import DebuggerException from dex.utils.Exceptions import BuildScriptException, HeuristicException @@ -155,10 +160,6 @@ def _init_debugger_controller(self): self.context.options.source_files.extend(list(new_source_files)) - # If we are not running a debugger, return the DextIR instead of a DebuggerController. - if self.context.options.skip_run: - return step_collection - if self.context.options.use_script: debugger_controller = ScriptDebuggerController( self.context, step_collection @@ -180,7 +181,8 @@ def _get_steps(self): if self.context.options.skip_run: self.context.logger.warning("Skipping run...") - return debugger_controller + assert isinstance(debugger_controller.step_collection, DextIR) + return debugger_controller.step_collection debugger_controller = run_debugger_subprocess( debugger_controller, self.context.working_directory.path ) @@ -254,6 +256,16 @@ def _record_structured_script_metric_results( with open(output_json_path, "w") as fp: json.dump(run_match.get_metric_json_output(), fp) + def _write_updated_structured_script( + self, test_name, rewritten_script: DexterScript + ): + """Write out the original script file, modified to replace any unknown expects with the actual observed + values.""" + if self.context.options.results_directory: + output_text_path = self._get_results_path(test_name) + with open(output_text_path, "w", encoding="utf-8") as fp: + fp.write(write_dexter_script_file(rewritten_script)) + def _record_test_and_display(self, test_case): """Output test case to o stream and record test case internally for handling later. @@ -313,6 +325,22 @@ def _run_test(self, test_name): return self._record_steps(test_name, steps) if self.context.options.use_script: + # Before evaluating, the script may contain "unknown" expects; if they should be rewritten, then do so + # first, and then use the rewritten script to evaluate. + script_writer = ScriptExpectWriter(self.context, steps) + if script_writer.new_script: + self.context.logger.note( + f"Rewrote script to add {script_writer.num_successful_rewrites} expected values." + ) + if script_writer.num_unsuccessful_rewrites: + self.context.logger.warning( + f"Failed to rewrite {script_writer.num_unsuccessful_rewrites} expected values." + ) + self._write_updated_structured_script( + test_name, script_writer.new_script + ) + steps.script = script_writer.new_script + # Then evaluate, using the new script if any was produced. run_match = DebuggerRunMatch(self.context, steps) self._record_structured_script_metric_results(test_name, run_match) self._record_successful_test_match(test_name, steps, run_match) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expect_list_expected.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expect_list_expected.cpp new file mode 100644 index 0000000000000..cec451fdd69c9 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expect_list_expected.cpp @@ -0,0 +1,128 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_expect_list_expected.cpp + +/// Test that Dexter can write lists of expected values for simple scalar +/// variables. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 3 expected values. + +// CHECK: total_watched_steps: 90 +// CHECK: correct_steps: 90 +// CHECK: incorrect_steps: 0 +// CHECK: seen_values: 86 +// CHECK: missing_values: 0 + +int main() { + int prev = 0; + int current = 0; + int next = 1; + for (int i = 0; i < 30; ++i) { + prev = current; // !dex_label loop + current = next; + next = prev + current; + } + return current; +} + +/* +--- +? !where {lines: !label 'loop'} +: !value 'prev': + - '0' + - '1' + - '2' + - '3' + - '5' + - '8' + - '13' + - '21' + - '34' + - '55' + - '89' + - '144' + - '233' + - '377' + - '610' + - '987' + - '1597' + - '2584' + - '4181' + - '6765' + - '10946' + - '17711' + - '28657' + - '46368' + - '75025' + - '121393' + - '196418' + - '317811' + !value 'current': + - '0' + - '1' + - '2' + - '3' + - '5' + - '8' + - '13' + - '21' + - '34' + - '55' + - '89' + - '144' + - '233' + - '377' + - '610' + - '987' + - '1597' + - '2584' + - '4181' + - '6765' + - '10946' + - '17711' + - '28657' + - '46368' + - '75025' + - '121393' + - '196418' + - '317811' + - '514229' + !value 'next': + - '1' + - '2' + - '3' + - '5' + - '8' + - '13' + - '21' + - '34' + - '55' + - '89' + - '144' + - '233' + - '377' + - '610' + - '987' + - '1597' + - '2584' + - '4181' + - '6765' + - '10946' + - '17711' + - '28657' + - '46368' + - '75025' + - '121393' + - '196418' + - '317811' + - '514229' + - '832040' +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expects_expected.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expects_expected.cpp new file mode 100644 index 0000000000000..2c04bcdc0fe0c --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_expects_expected.cpp @@ -0,0 +1,51 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_expects_expected.cpp + +/// Test that when we have a Dexter test with missing/unknown expected values, +/// Dexter produces a modified test file that is identical except for a modified +/// script section. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 6 expected values. +// CHECK: Failed to rewrite 2 expected values. + +// CHECK: total_watched_steps: 7 +// CHECK: correct_steps: 6 +// CHECK: incorrect_steps: 1 +// CHECK: seen_values: 6 +// CHECK: missing_values: 2 + +int multiply(int b, int a) { + int result = a * b; + return result; // !dex_label mul_ret +} + +int main() { + int a = 6; + int b = 7; + int c = multiply(a, b); + return c; // !dex_label main_ret +} +// !dex_label never_reached +/* +--- +? !where {lines: !label 'mul_ret'} +: !value 'a': '7' + !value 'b': '6' + !value 'result': '42' +? !where {lines: !label 'main_ret'} +: !value 'a': '6' + !value 'b': '7' + !value 'c': '42' + !value 'not_real': null +? !where {lines: !label 'never_reached'} +: !value 'a': null +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp new file mode 100644 index 0000000000000..525889107895b --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp @@ -0,0 +1,46 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} \ +// RUN: %S/Inputs/rewrite_multiple_scripts_expected.cpp + +/// Test that when a file contains more than one valid YAML script (but only one +/// Dexter script), the existing YAML is printed correctly. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 1 expected values. + +// CHECK: total_watched_steps: 1 +// CHECK: correct_steps: 1 +// CHECK: incorrect_steps: 0 +// CHECK: seen_values: 1 +// CHECK: missing_values: 0 + +/* +--- +hr: # 1998 hr ranking +- Mark McGwire +- Sammy Sosa +# 1998 rbi ranking +rbi: +- Sammy Sosa +- Ken Griffey +... +*/ + +int main() { + int ret = 0; + return ret; // !dex_label ret +} + +/* +--- +? !where {lines: !label 'ret'} +: !value 'ret': '0' +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp new file mode 100644 index 0000000000000..177289e1c3ff2 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp @@ -0,0 +1,7 @@ + +int main() { + int i = 0; + i += 1; // !dex_label start + i += 1; + return i; // !dex_label end +} diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test.dex b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test.dex new file mode 100644 index 0000000000000..4501532871cc9 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test.dex @@ -0,0 +1,2 @@ +!where {file: 'simple_prog.cpp', lines: !range [!label start, !label end]}: + ? !value i \ No newline at end of file diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test_expected.dex b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test_expected.dex new file mode 100644 index 0000000000000..c1cf5e87915a5 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/whole_file_test_expected.dex @@ -0,0 +1,6 @@ +? !where {file: simple_prog.cpp, lines: !range [!label 'start', !label 'end']} +: !value 'i': + - '0' + - '1' + - '2' +... diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expect_list.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expect_list.cpp new file mode 100644 index 0000000000000..dee5c936522e5 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expect_list.cpp @@ -0,0 +1,42 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_expect_list_expected.cpp + +/// Test that Dexter can write lists of expected values for simple scalar +/// variables. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 3 expected values. + +// CHECK: total_watched_steps: 90 +// CHECK: correct_steps: 90 +// CHECK: incorrect_steps: 0 +// CHECK: seen_values: 86 +// CHECK: missing_values: 0 + +int main() { + int prev = 0; + int current = 0; + int next = 1; + for (int i = 0; i < 30; ++i) { + prev = current; // !dex_label loop + current = next; + next = prev + current; + } + return current; +} + +/* +--- +!where {lines: !label loop}: + ? !value prev + ? !value current + ? !value next +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expects.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expects.cpp new file mode 100644 index 0000000000000..256804e114ad7 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_expects.cpp @@ -0,0 +1,52 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} %S/Inputs/rewrite_expects_expected.cpp + +/// Test that when we have a Dexter test with missing/unknown expected values, +/// Dexter produces a modified test file that is identical except for a modified +/// script section. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 6 expected values. +// CHECK: Failed to rewrite 2 expected values. + +// CHECK: total_watched_steps: 7 +// CHECK: correct_steps: 6 +// CHECK: incorrect_steps: 1 +// CHECK: seen_values: 6 +// CHECK: missing_values: 2 + +int multiply(int b, int a) { + int result = a * b; + return result; // !dex_label mul_ret +} + +int main() { + int a = 6; + int b = 7; + int c = multiply(a, b); + return c; // !dex_label main_ret +} +// !dex_label never_reached +/* +--- +# Comments in the Dexter script are not preserved. +!where {lines: !label mul_ret}: + ? !value a + ? !value b + ? !value result +!where {lines: !label main_ret}: + ? !value a + ? !value b + ? !value c + ? !value not_real +!where {lines: !label never_reached}: + ? !value a +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp new file mode 100644 index 0000000000000..f11a6d619e1ff --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp @@ -0,0 +1,46 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %dexter_regression_test_cxx_build %s -o %t/test +// RUN: %dexter_regression_test_run --use-script --binary %t/test \ +// RUN: --results-directory %t/results -- %s 2>&1 | FileCheck %s +// RUN: diff %t/results/%{s:basename} \ +// RUN: %S/Inputs/rewrite_multiple_scripts_expected.cpp + +/// Test that when a file contains more than one valid YAML script (but only one +/// Dexter script), the existing YAML is printed correctly. + +/// NB: The exact contents of this file are compared against the expect file in +/// the Inputs/ directory; any changes to this file, including comments, +/// will require updating the corresponding expected file. + +// CHECK: Rewrote script to add 1 expected values. + +// CHECK: total_watched_steps: 1 +// CHECK: correct_steps: 1 +// CHECK: incorrect_steps: 0 +// CHECK: seen_values: 1 +// CHECK: missing_values: 0 + +/* +--- +hr: # 1998 hr ranking +- Mark McGwire +- Sammy Sosa +# 1998 rbi ranking +rbi: +- Sammy Sosa +- Ken Griffey +... +*/ + +int main() { + int ret = 0; + return ret; // !dex_label ret +} + +/* +--- +!where {lines: !label ret}: + ? !value ret +... +*/ diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/whole_file.test b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/whole_file.test new file mode 100644 index 0000000000000..7168e4a7dd8ac --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/whole_file.test @@ -0,0 +1,22 @@ +RUN: rm -rf %t +RUN: mkdir %t +RUN: %dexter_regression_test_cxx_build %S/Inputs/simple_prog.cpp -o %t/test +RUN: %dexter_regression_test_run --use-script --binary %t/test \ +RUN: --results-directory %t/results --source-root-dir %S/Inputs -- \ +RUN: %S/Inputs/whole_file_test.dex 2>&1 | FileCheck %s +RUN: diff %t/results/whole_file_test.dex %S/Inputs/whole_file_test_expected.dex + +Test that script rewriting still works when the test file is pure YAML, rather +than a YAML Dexter script embedded in another file. + +NB: The exact contents of this file are compared against the expect file in + the Inputs/ directory; any changes to this file, including comments, + will require updating the corresponding expected file. + +CHECK: Rewrote script to add 1 expected values. + +CHECK: total_watched_steps: 3 +CHECK: correct_steps: 3 +CHECK: incorrect_steps: 0 +CHECK: seen_values: 3 +CHECK: missing_values: 0 \ No newline at end of file >From 9d0538ac6347549a76e94879d0c9e384ec1baed9 Mon Sep 17 00:00:00 2001 From: Stephen Tozer <[email protected]> Date: Wed, 10 Jun 2026 13:23:51 +0100 Subject: [PATCH 2/2] format --- .../Inputs/rewrite_multiple_scripts_expected.cpp | 4 ++-- .../scripts/rewriting/Inputs/simple_prog.cpp | 8 ++++---- .../scripts/rewriting/rewrite_multiple_scripts.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp index 525889107895b..134d32f93a5a4 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/rewrite_multiple_scripts_expected.cpp @@ -34,8 +34,8 @@ hr: # 1998 hr ranking */ int main() { - int ret = 0; - return ret; // !dex_label ret + int ret = 0; + return ret; // !dex_label ret } /* diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp index 177289e1c3ff2..72567b9db3e51 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/Inputs/simple_prog.cpp @@ -1,7 +1,7 @@ int main() { - int i = 0; - i += 1; // !dex_label start - i += 1; - return i; // !dex_label end + int i = 0; + i += 1; // !dex_label start + i += 1; + return i; // !dex_label end } diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp index f11a6d619e1ff..ce3b8f6b2db38 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/scripts/rewriting/rewrite_multiple_scripts.cpp @@ -34,8 +34,8 @@ hr: # 1998 hr ranking */ int main() { - int ret = 0; - return ret; // !dex_label ret + int ret = 0; + return ret; // !dex_label ret } /* _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
