https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/218565
>From e55898d8599cc300ac8f486abdecde3dcae70f38 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Tue, 25 Aug 2026 01:08:12 +0100 Subject: [PATCH 1/3] [lldb] Fix SBAPI breakage in SBFrame::GetValueForVariablePath SBFrame::GetValueForVariablePath added new parameters in commit 80fffd527c20ac8970fbffc37c674caa17faa815. This caused an ABI break because the ABI is based on the function's mangled name, which changes when parameters are added. Existing users depend on the original symbol being present in the library. changing from ```cpp // _ZN4lldb7SBFrame23GetValueForVariablePathEPKcNS_16DynamicValueTypeE lldb::SBFrame::GetValueForVariablePath(char const*, lldb::DynamicValueType) ``` to ```cpp // _ZN4lldb7SBFrame23GetValueForVariablePathEPKcNS_16DynamicValueTypeENS_7DILModeE` lldb::SBFrame::GetValueForVariablePath(char const*, lldb::DynamicValueType, lldb::DILMode) ``` Normally, this can be fixed by manually overloading the function: ```cpp GetValueForVariablePath(const char *, DynamicValueType, lldb::DILMode = lldb::eDILModeFull); ``` to ```cpp GetValueForVariablePath(const char *, DynamicValueType); GetValueForVariablePath(const char *, DynamicValueType, lldb::DILMode mode); ``` with the two-argument overload setting the mode in the implementation. However, this approach does not work for the Python API because enums are converted to integers. The four C++ overloads: ```cpp GetValueForVariablePath(const char *); GetValueForVariablePath(const char *, lldb::DILMode); GetValueForVariablePath(const char *, DynamicValueType); GetValueForVariablePath(const char *, DynamicValueType, lldb::DILMode mode); ``` would appear in python as: ```py GetValueForVariablePath(str) GetValueForVariablePath(str, int) GetValueForVariablePath(str, int) GetValueForVariablePath(str, int, int) ``` As a result, there is no way to distinguish between `lldb::DILMode` and `lldb::DynamicValueType` when they are passed as the second parameter. Existing test did not catch this because there is not test covering `GetValueForVariablePath(const char *, DynamicValueType)`). Instead I added an new function called ```cpp GetValueForVariablePathWithMode(const char*, lldb::DILMode) GetValueForVariablePathWithMode(const char*, lldb::DILMode, DynamicValueType) ``` and a python helper. ```python class SBFrame: def var_with_mode(self, var_path: str, mode: int, use_dynamic: int, /): ... ``` mirroring the existing `SBFrame.var` helper function. --- lldb/bindings/interface/SBFrameExtensions.i | 5 ++ lldb/include/lldb/API/SBFrame.h | 17 +++--- lldb/source/API/SBFrame.cpp | 57 ++++++++++++++++-- .../AddressOf/TestFrameVarDILAddressOf.py | 4 +- .../TestFrameVarDILArraySubscript.py | 13 +++- .../MemberOf/TestFrameVarDILMemberOf.py | 8 +-- .../TestFrameVarDILPointerDereference.py | 4 +- .../Arithmetic/TestFrameVarDILArithmetic.py | 6 +- .../expr/Bitwise/TestFrameVarDILBitwise.py | 10 ++- .../var-dil/expr/Casts/TestFrameVarDILCast.py | 4 +- .../diagnostic_reporting/dump.core | Bin 0 -> 1562 bytes .../Handler/EvaluateRequestHandler.cpp | 4 +- lldb/tools/lldb-dap/SourceBreakpoint.cpp | 4 +- 13 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 lldb/test/API/functionalities/diagnostic_reporting/dump.core diff --git a/lldb/bindings/interface/SBFrameExtensions.i b/lldb/bindings/interface/SBFrameExtensions.i index 0c79b98b290f2..b9c6c79b7b1d7 100644 --- a/lldb/bindings/interface/SBFrameExtensions.i +++ b/lldb/bindings/interface/SBFrameExtensions.i @@ -44,6 +44,11 @@ STRING_EXTENSION_OUTSIDE(SBFrame) '''Calls through to lldb.SBFrame.GetValueForVariablePath() and returns a value that represents the variable expression path''' return self.GetValueForVariablePath(var_expr_path) + + def var_with_mode(self, var_path: str, mode: int, use_dynamic = None, /): + if use_dynamic is None: + return self.GetValueForVariablePathWithMode(var_path, mode) + return self.GetValueForVariablePathWithMode(var_path, mode, use_dynamic) def get_registers_access(self): class registers_access(object): diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 7094ce3ad8fb7..4d67ac74eab12 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -182,16 +182,19 @@ class LLDB_API SBFrame { // expression result and is not a constant object like // SBFrame::EvaluateExpression(...) returns, but a child object of the // variable value. - lldb::SBValue - GetValueForVariablePath(const char *var_expr_cstr, - DynamicValueType use_dynamic, - lldb::DILMode mode = lldb::eDILModeFull); + lldb::SBValue GetValueForVariablePath(const char *var_path, + DynamicValueType use_dynamic); /// The version that doesn't supply a 'use_dynamic' value will use the /// target's default. - lldb::SBValue - GetValueForVariablePath(const char *var_path, - lldb::DILMode mode = lldb::eDILModeFull); + lldb::SBValue GetValueForVariablePath(const char *var_path); + + lldb::SBValue GetValueForVariablePathWithMode(const char *var_path, + lldb::DILMode mode, + DynamicValueType use_dynamic); + + lldb::SBValue GetValueForVariablePathWithMode(const char *var_path, + lldb::DILMode mode); /// Find variables, register sets, registers, or persistent variables using /// the frame as the scope. diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index 9158685f78c04..6debaa12dcfe6 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -365,8 +365,7 @@ void SBFrame::Clear() { m_opaque_sp->Clear(); } -lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, - lldb::DILMode mode) { +lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) { LLDB_INSTRUMENT_VA(this, var_path); SBValue sb_value; @@ -380,14 +379,13 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, if (StackFrame *frame = exe_ctx->GetFramePtr()) { lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); - sb_value = GetValueForVariablePath(var_path, use_dynamic, mode); + sb_value = GetValueForVariablePath(var_path, use_dynamic); } return sb_value; } lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, - DynamicValueType use_dynamic, - lldb::DILMode mode) { + DynamicValueType use_dynamic) { LLDB_INSTRUMENT_VA(this, var_path, use_dynamic); SBValue sb_value; @@ -402,6 +400,55 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, return sb_value; } + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + VariableSP var_sp; + Status error; + ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath( + var_path, eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, error, lldb::eDILModeFull)); + sb_value.SetSP(value_sp, use_dynamic); + } + return sb_value; +} + +lldb::SBValue SBFrame::GetValueForVariablePathWithMode(const char *var_path, + lldb::DILMode mode) { + LLDB_INSTRUMENT_VA(this, var_path, mode); + + SBValue sb_value; + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_value; + } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + lldb::DynamicValueType use_dynamic = + frame->CalculateTarget()->GetPreferDynamicValue(); + sb_value = GetValueForVariablePathWithMode(var_path, mode, use_dynamic); + } + return sb_value; +} + +lldb::SBValue SBFrame::GetValueForVariablePathWithMode( + const char *var_path, lldb::DILMode mode, DynamicValueType use_dynamic) { + LLDB_INSTRUMENT_VA(this, var_path, mode, use_dynamic); + + SBValue sb_value; + if (var_path == nullptr || var_path[0] == '\0') { + return sb_value; + } + + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_value; + } + if (StackFrame *frame = exe_ctx->GetFramePtr()) { VariableSP var_sp; Status error; diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py index bfe29370b4704..4d401dadbe064 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py +++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py @@ -39,7 +39,7 @@ def test_frame_var(self): # Check that '&' is not allowed in simple mode, but allowed in legacy mode frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("&x", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("&x", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode("&x", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("&x", lldb.eDILModeLegacy) self.assertFailure(simple.GetError()) self.assertSuccess(legacy.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py index 6f3bbfb970aa4..d0477ac846f70 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -89,10 +89,19 @@ def test_subscript(self): # Check that subscription is not allowed in simple mode, but allowed in legacy mode frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode( + "int_arr[0]", lldb.eDILModeSimple + ) + simple_other = frame.var_with_mode("int_arr[0]", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode( + "int_arr[0]", lldb.eDILModeLegacy + ) + legacy_other = frame.var_with_mode("int_arr[0]", lldb.eDILModeLegacy) + self.assertFailure(simple.GetError()) + self.assertFailure(simple_other.GetError()) self.assertSuccess(legacy.GetError()) + self.assertSuccess(legacy_other.GetError()) def test_subscript_synthetic(self): self.build() diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py index d37ce0bbc4201..91ec777a5d2e4 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py +++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py @@ -52,13 +52,13 @@ def test_frame_var(self): # Check that '.' is allowed in both simple and legacy modes frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("s.x", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("s.x", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode("s.x", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("s.x", lldb.eDILModeLegacy) self.assertSuccess(simple.GetError()) self.assertSuccess(legacy.GetError()) # Check that '->' is not allowed in simple mode, but allowed in legacy mode - simple = frame.GetValueForVariablePath("sp->x", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("sp->x", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode("sp->x", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("sp->x", lldb.eDILModeLegacy) self.assertFailure(simple.GetError()) self.assertSuccess(legacy.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py index e18e18e146a46..0fd7534e07fc6 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py +++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py @@ -49,7 +49,7 @@ def test_frame_var(self): # Check that * is not allowed in simple mode, but allowed in legacy mode frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode("*p_int0", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("*p_int0", lldb.eDILModeLegacy) self.assertFailure(simple.GetError()) self.assertSuccess(legacy.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py index 7729c1ce23d7d..94268ecf46b27 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -169,9 +169,9 @@ def test_arithmetic(self): # Check that binary * is allowed only in full mode frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("x * 2", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("x * 2", lldb.eDILModeLegacy) - full = frame.GetValueForVariablePath("x * 2", lldb.eDILModeFull) + simple = frame.GetValueForVariablePathWithMode("x * 2", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("x * 2", lldb.eDILModeLegacy) + full = frame.GetValueForVariablePathWithMode("x * 2", lldb.eDILModeFull) self.assertFailure(simple.GetError()) self.assertFailure(legacy.GetError()) self.assertSuccess(full.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py index 5ce2954893f9f..abd5091b28fb2 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Bitwise/TestFrameVarDILBitwise.py @@ -107,9 +107,13 @@ def test_bitwise(self): # Check that bitwise & is allowed only in full mode frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeLegacy) - full = frame.GetValueForVariablePath("i64 & 1", lldb.eDILModeFull) + simple = frame.GetValueForVariablePathWithMode("i64 & 1", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("i64 & 1", lldb.eDILModeLegacy) + legacy_other = frame.var_with_mode("i64 & 1", lldb.eDILModeLegacy) + full = frame.GetValueForVariablePathWithMode("i64 & 1", lldb.eDILModeFull) + full_other = frame.var_with_mode("i64 & 1", lldb.eDILModeFull) self.assertFailure(simple.GetError()) self.assertFailure(legacy.GetError()) + self.assertFailure(legacy_other.GetError()) self.assertSuccess(full.GetError()) + self.assertSuccess(full_other.GetError()) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py index 567981cd670e9..385623eab1c71 100644 --- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py +++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py @@ -303,8 +303,8 @@ def test_type_cast(self): # Check that casts are not allowed in both simple and legacy modes frame = thread.GetFrameAtIndex(0) - simple = frame.GetValueForVariablePath("(char)a", lldb.eDILModeSimple) - legacy = frame.GetValueForVariablePath("(char)a", lldb.eDILModeLegacy) + simple = frame.GetValueForVariablePathWithMode("(char)a", lldb.eDILModeSimple) + legacy = frame.GetValueForVariablePathWithMode("(char)a", lldb.eDILModeLegacy) self.assertFailure(simple.GetError()) self.assertFailure(legacy.GetError()) diff --git a/lldb/test/API/functionalities/diagnostic_reporting/dump.core b/lldb/test/API/functionalities/diagnostic_reporting/dump.core new file mode 100644 index 0000000000000000000000000000000000000000..c47e4dc0bc5b9cc38ba17c58e6bfd2f7dae0d912 GIT binary patch literal 1562 zcmeZu@eP=~oPmLvfq_8*h|vK^+ysbSfS3h{3xL>zm4SgBNLv8$2_TyhNH^L632b12 z$?HG$5Qz#PbpdDyh$a<qGYEjS7$a$9V*=Ss9)OsJsaIeT#Jw;YrsBgKi1>k(QhQ!P z{R5I)AZ8EdyD>pjPLgo|GYpm>{0)(TkjG>p^7WvA#R3}S85sWm$09**A)q7vLjago zMFRw#gQlOLdKxGg48cePZ;cu`2txo;5;;IzR)8p%b^yyqasjAp1pych;Xz0)AeG3V z$B@rZ%1{Dib0SMZ*^Es<1{;Lqo|;#hnV0IBSCX28tW*TZ@nOhh$OD>F!Jxok3^daa INHQ=00H=RX?f?J) literal 0 HcmV?d00001 diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp index 7537eca72ec25..878905ed5adc2 100644 --- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp @@ -55,8 +55,8 @@ static lldb::SBValue EvaluateVariableExpression(lldb::SBTarget &target, // Check if it is a variable or an expression path for a variable. i.e. // 'foo->bar' finds the 'bar' variable. It is more reliable than the // expression parser in many cases and it is faster. - value = frame.GetValueForVariablePath( - expression_cstr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy); + value = frame.GetValueForVariablePathWithMode( + expression_cstr, lldb::eDILModeLegacy, lldb::eDynamicDontRunTarget); if (value || !run_as_expression) return value; diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp index 77da2966723e6..622071a8ac31e 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp +++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp @@ -399,8 +399,8 @@ bool SourceBreakpoint::BreakpointHitCallback( // evaluation const std::string &expr_str = messagePart.text; const char *expr = expr_str.c_str(); - lldb::SBValue value = frame.GetValueForVariablePath( - expr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy); + lldb::SBValue value = frame.GetValueForVariablePathWithMode( + expr, lldb::eDILModeLegacy, lldb::eDynamicDontRunTarget); if (value.GetError().Fail()) value = frame.EvaluateExpression(expr); output += VariableDescription( >From 008b94dd42ed0fff4d8b9da640ecda7195c4a570 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 26 Aug 2026 00:15:07 +0100 Subject: [PATCH 2/3] [lldb] Add regression test for GetValueForVariablePath --- lldb/bindings/interface/SBFrameExtensions.i | 6 +- .../functionalities/var_path/TestVarPath.py | 67 +++++++++++++++++++ .../API/functionalities/var_path/main.cpp | 33 +++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/lldb/bindings/interface/SBFrameExtensions.i b/lldb/bindings/interface/SBFrameExtensions.i index b9c6c79b7b1d7..64ca132d159f5 100644 --- a/lldb/bindings/interface/SBFrameExtensions.i +++ b/lldb/bindings/interface/SBFrameExtensions.i @@ -40,10 +40,12 @@ STRING_EXTENSION_OUTSIDE(SBFrame) def get_statics(self): return self.GetVariables(False,False,True,False) - def var(self, var_expr_path): + def var(self, var_expr_path, use_dynamic=None): '''Calls through to lldb.SBFrame.GetValueForVariablePath() and returns a value that represents the variable expression path''' - return self.GetValueForVariablePath(var_expr_path) + if use_dynamic is None: + return self.GetValueForVariablePath(var_expr_path) + return self.GetValueForVariablePath(var_expr_path, use_dynamic) def var_with_mode(self, var_path: str, mode: int, use_dynamic = None, /): if use_dynamic is None: diff --git a/lldb/test/API/functionalities/var_path/TestVarPath.py b/lldb/test/API/functionalities/var_path/TestVarPath.py index 53c45f57f517d..a08b5e1d2692e 100644 --- a/lldb/test/API/functionalities/var_path/TestVarPath.py +++ b/lldb/test/API/functionalities/var_path/TestVarPath.py @@ -100,6 +100,8 @@ def do_test(self): self.verify_point(frame, "pt_ptr[0]", "Point", 3030, 4040) self.verify_point(frame, "pt_ptr[1]", "Point", 5050, 6060) # Test arrays + v_helper = frame.var("points") + self.assertSuccess(v_helper.GetError(), "Make sure we find 'points'") v = frame.GetValueForVariablePath("points") self.assertSuccess(v.GetError(), "Make sure we find 'points'") self.verify_point(frame, "points[0]", "Point", 1010, 2020) @@ -121,3 +123,68 @@ def do_test(self): self.assertTrue( v.GetError().Fail(), "Make sure we don't find 'pt_sp->not_valid_child'" ) + + def test_frame_var_use_dynamic(self): + """Test `SBFrame.GetValueForVariablePath` return the current value and variable type.""" + self.build() + (_, _, thread, _) = lldbutil.run_to_source_breakpoint( + self, "// Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + frame: lldb.SBFrame = thread.GetFrameAtIndex(0) + + # Verify concrete types. + concrete = [ + ("shape", "Shape"), + ("shape_ref", "Shape &"), + ("shape_ptr", "Shape *"), + ("circle", "Circle"), + ("circle_ref", "Circle &"), + ("circle_ptr", "Circle *"), + ] + for name, expected_type in concrete: + for mode in (lldb.eNoDynamicValues, lldb.eDynamicDontRunTarget): + val: lldb.SBValue = frame.GetValueForVariablePath(name, mode) + self.assertSuccess(val.GetError(), f"Make sure we find {name!r}") + self.assertEqual(val.GetTypeName(), expected_type) + + # Test the helper. + val: lldb.SBValue = frame.var(name, mode) + self.assertTrue(val.GetError().Success(), f"Make sure we find {name!r}") + self.assertEqual(val.GetTypeName(), expected_type) + + # Verify dynamic types. + polymorphic = [ + ("circle_as_shape_ref", "Shape &", "Circle &"), + ("circle_as_shape_ptr", "Shape *", "Circle *"), + ("circle_as_drawable_ref", "Drawable &", "Circle &"), + ("circle_as_drawable_ptr", "Drawable *", "Circle *"), + ] + for name, static_type, dynamic_type in polymorphic: + static = frame.GetValueForVariablePath(name, lldb.eNoDynamicValues) + static_helper: lldb.SBValue = frame.var(name, lldb.eNoDynamicValues) + for val in (static, static_helper): + self.assertSuccess(val.GetError(), f"find {name!r} (static)") + self.assertEqual(static_type, val.GetTypeName()) + self.assertNotIn("Circle", val.GetTypeName()) + self.assertFalse( + val.GetChildMemberWithName("circle_val").IsValid(), + f"{name!r} should not expose circle_val under eNoDynamicValues", + ) + + dynamic = frame.GetValueForVariablePath(name, lldb.eDynamicDontRunTarget) + dynamic_helper: lldb.SBValue = frame.var(name, lldb.eDynamicDontRunTarget) + for val in (dynamic, dynamic_helper): + self.assertSuccess(val.GetError(), f"find {name!r} (dynamic)") + self.assertEqual( + dynamic_type, + val.GetTypeName(), + f"'{name}' should resolve to Circle under eDynamicDontRunTarget", + ) + circle_val = val.GetChildMemberWithName("circle_val") + self.assertTrue( + circle_val.IsValid(), + f"{name!r} should expose circle_val under eDynamicDontRunTarget", + ) + + # Verify we can fetch the child from the static type. + self.assertEqual(circle_val.GetValueAsSigned(), 20) diff --git a/lldb/test/API/functionalities/var_path/main.cpp b/lldb/test/API/functionalities/var_path/main.cpp index 0ea19cfcfea53..daf1d95114657 100644 --- a/lldb/test/API/functionalities/var_path/main.cpp +++ b/lldb/test/API/functionalities/var_path/main.cpp @@ -1,5 +1,23 @@ #include <memory> +class Shape { +public: + virtual ~Shape() = default; + int shape_val = 10; +}; + +class Drawable { +public: + virtual ~Drawable() = default; + virtual void draw() = 0; +}; + +class Circle : public Shape, public Drawable { +public: + int circle_val = 20; + void draw() override {} +}; + struct Point { int x, y; }; @@ -10,6 +28,21 @@ int main() { Point *pt_ptr = &points[1]; Point &pt_ref = pt; std::shared_ptr<Point> pt_sp(new Point{111,222}); + + Shape shape{}; + Shape &shape_ref = shape; + Shape *shape_ptr = &shape; + + Circle circle{}; + Circle &circle_ref = circle; + Circle *circle_ptr = &circle; + + Shape &circle_as_shape_ref = circle; + Shape *circle_as_shape_ptr = &circle; + + Drawable &circle_as_drawable_ref = circle; + Drawable *circle_as_drawable_ptr = &circle; + return 0; // Set a breakpoint here } >From 80bae293c028fccaaafa56591e4dfbba1321b67b Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Thu, 27 Aug 2026 12:55:50 +0100 Subject: [PATCH 3/3] skip on windows --- lldb/test/API/functionalities/var_path/TestVarPath.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lldb/test/API/functionalities/var_path/TestVarPath.py b/lldb/test/API/functionalities/var_path/TestVarPath.py index a08b5e1d2692e..9813c966f6f4c 100644 --- a/lldb/test/API/functionalities/var_path/TestVarPath.py +++ b/lldb/test/API/functionalities/var_path/TestVarPath.py @@ -124,6 +124,10 @@ def do_test(self): v.GetError().Fail(), "Make sure we don't find 'pt_sp->not_valid_child'" ) + @expectedFailureAll( + oslist=["windows"], + bugnumber="https://github.com/llvm/llvm-project/issues/25037", + ) def test_frame_var_use_dynamic(self): """Test `SBFrame.GetValueForVariablePath` return the current value and variable type.""" self.build() _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
