[llvm] [Dexter] Avoid incorrect state matching against frames below main (PR #206732)

Stephen Tozer via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 07:17:10 PDT 2026


https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/206732

>From 3201bfb1e75690447e6dbbbd0e0eb1660a2d32b3 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Tue, 30 Jun 2026 13:53:16 +0100
Subject: [PATCH 1/2] [Dexter] Avoid incorrect state matching against frames
 below main

This patch fixes an error that caused some Dexter test failures, driven by
two separate causes. The first issue is that frames below main were
appearing in the program stacktrace; while Dexter tries to filter frames
below main during the stacktrace collection step based on a pre-written
list, this list may not be comprehensive enough, as the symbol
"___lldb_unnamed_symbol_2a150" has also appeared. In order to guard against
this and future cases that might appear, this patch adds a check to Dexter
for "presentationHint: deemphasize" in the DAP response; this is added by
LLDB (and other dap-based debuggers) as a hint that the frame is not user
source, and should be a generally useful way of avoiding evaluating frames
that are not wanted.

The second issue is a mismatch between the breakpoint-setting logic and the
state-matching logic: the former allows root !where nodes to omit the "file"
field, using the script file as a default file. The state matching logic
does not perform any checking for an omitted file. Together, this means that
we may correctly set breakpoints for e.g. "test.cpp:10", but when we go to
match against frames, the !where node may match against some other line-10
that appears below (e.g. as a caller of) the frame at test.cpp:10. This
patch copies the default_file logic to state matching, meaning that we will
only match against the intended frame.
---
 .../debuginfo-tests/dexter/dex/debugger/DAP.py         |  9 +++++----
 .../dexter/dex/evaluation/StateMatch.py                | 10 +++++++---
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
index 0dfded0ff881a..ac56dbe19eb1c 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
@@ -969,12 +969,13 @@ def get_stack_frames(self, step_index: int) -> StepIR:
         frames = []
 
         for stackframe in stackframes:
+            # Some frames are marked "deemphasize" to indicate that they are not interesting; these frames can be
+            # skipped by Dexter.
+            if stackframe.get("source", {}).get("presentationHint") == "deemphasize":
+                continue
             # No source, skip the frame! Currently I've only observed this for frames below main, so we break here; if
             # it happens elsewhere, then this will break more stuff and we'll come up with a better solution.
-            if (
-                stackframe.get("source") is None
-                or stackframe["source"].get("path") is None
-            ):
+            if stackframe.get("source", {}).get("path") is None:
                 break
 
             loc_dict = {
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
index 2be87590b978c..20c3933e2a43b 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/evaluation/StateMatch.py
@@ -9,7 +9,7 @@
 
 from dataclasses import dataclass, field
 import os
-from typing import Dict, List, Tuple
+from typing import Dict, List, Optional, Tuple
 
 from dex.dextIR import FrameIR, StepIR
 from dex.test_script import DexterScript, Scope
@@ -29,9 +29,13 @@ def match_where_to_frame(
     where: Where,
     frame: FrameIR,
     labels: FileLabels,
+    default_path: Optional[str] = None,
 ) -> bool:
     """A very simple matcher, returns True iff `where` matches `frame`."""
-    if where.file is not None and not is_subpath(where.file, frame.loc.path):
+    file = where.file
+    if not file and where.lines and not where.function:
+        file = default_path
+    if file is not None and not is_subpath(file, frame.loc.path):
         return False
     if where.function is not None:
         fn = frame.function
@@ -100,7 +104,7 @@ def get_active_wheres(where: Where, scope: Scope):
         matching_frame_idx = None
         for frame_idx, frame in reversed(list(enumerate(step_info.frames))):
             labels = script.get_labels(expected_file or frame.loc.path)
-            if match_where_to_frame(where, frame, labels):
+            if match_where_to_frame(where, frame, labels, script.root_scope.file):
                 matching_frame_idx = frame_idx
                 break
 

>From 5a90b56f0bbdfaa333ad7fcfca27595b2afcfd35 Mon Sep 17 00:00:00 2001
From: Stephen Tozer <stephen.tozer at sony.com>
Date: Tue, 30 Jun 2026 15:16:52 +0100
Subject: [PATCH 2/2] Add comment

---
 .../debuginfo-tests/dexter/dex/debugger/DAP.py                | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
index ac56dbe19eb1c..7e987173bfd28 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DAP.py
@@ -971,6 +971,10 @@ def get_stack_frames(self, step_index: int) -> StepIR:
         for stackframe in stackframes:
             # Some frames are marked "deemphasize" to indicate that they are not interesting; these frames can be
             # skipped by Dexter.
+            # NB: This is by no means guaranteed to be set by the debug adapter, so is not a perfectly reliable check;
+            #     however, if it *is* set, it's generally a safe bet that we are not looking at user code and so can
+            #     skip this frame. We may check for more presentationHint values in future if/as we add support for more
+            #     debug adapters.
             if stackframe.get("source", {}).get("presentationHint") == "deemphasize":
                 continue
             # No source, skip the frame! Currently I've only observed this for frames below main, so we break here; if



More information about the llvm-commits mailing list