Author: Igor Kudrin Date: 2025-06-27T14:30:24-07:00 New Revision: 442f99d7698a4ca87ebb16cb22df610c45d4698a
URL: https://github.com/llvm/llvm-project/commit/442f99d7698a4ca87ebb16cb22df610c45d4698a DIFF: https://github.com/llvm/llvm-project/commit/442f99d7698a4ca87ebb16cb22df610c45d4698a.diff LOG: [lldb] Fix evaluating expressions without JIT in an object context (#145599) If a server does not support allocating memory in an inferior process or when debugging a core file, evaluating an expression in the context of a value object results in an error: ``` error: <lldb wrapper prefix>:43:1: use of undeclared identifier '$__lldb_class' 43 | $__lldb_class::$__lldb_expr(void *$__lldb_arg) | ^ ``` Such expressions require a live address to be stored in the value object. However, `EntityResultVariable::Dematerialize()` only sets `ret->m_live_sp` if JIT is available, even if the address points to the process memory and no custom allocations were made. Similarly, `EntityPersistentVariable::Dematerialize()` tries to deallocate memory based on the same check, resulting in an error if the memory was not previously allocated in `EntityPersistentVariable::Materialize()`. As an unintended bonus, the patch also fixes a FIXME case in `TestCxxChar8_t.py`. Added: lldb/test/API/functionalities/postmortem/elf-core/expr/TestExpr.py lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.core lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.out lldb/test/API/functionalities/postmortem/elf-core/expr/main.cpp Modified: lldb/include/lldb/Expression/IRMemoryMap.h lldb/source/Expression/IRMemoryMap.cpp lldb/source/Expression/Materializer.cpp lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py Removed: ################################################################################ diff --git a/lldb/include/lldb/Expression/IRMemoryMap.h b/lldb/include/lldb/Expression/IRMemoryMap.h index acbffd1e40b94..58b95c56c1c36 100644 --- a/lldb/include/lldb/Expression/IRMemoryMap.h +++ b/lldb/include/lldb/Expression/IRMemoryMap.h @@ -51,10 +51,13 @@ class IRMemoryMap { ///only in the process. }; + // If 'policy' is 'eAllocationPolicyMirror' but it is impossible to allocate + // memory in the process, 'eAllocationPolicyHostOnly' will be used instead. + // The actual policy is returned via 'used_policy'. llvm::Expected<lldb::addr_t> Malloc(size_t size, uint8_t alignment, uint32_t permissions, - AllocationPolicy policy, - bool zero_memory); + AllocationPolicy policy, bool zero_memory, + AllocationPolicy *used_policy = nullptr); void Leak(lldb::addr_t process_address, Status &error); void Free(lldb::addr_t process_address, Status &error); diff --git a/lldb/source/Expression/IRMemoryMap.cpp b/lldb/source/Expression/IRMemoryMap.cpp index f500272cfb302..150699352a2e3 100644 --- a/lldb/source/Expression/IRMemoryMap.cpp +++ b/lldb/source/Expression/IRMemoryMap.cpp @@ -319,10 +319,10 @@ IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, } } -llvm::Expected<lldb::addr_t> IRMemoryMap::Malloc(size_t size, uint8_t alignment, - uint32_t permissions, - AllocationPolicy policy, - bool zero_memory) { +llvm::Expected<lldb::addr_t> +IRMemoryMap::Malloc(size_t size, uint8_t alignment, uint32_t permissions, + AllocationPolicy policy, bool zero_memory, + AllocationPolicy *used_policy) { lldb_private::Log *log(GetLog(LLDBLog::Expressions)); lldb::ProcessSP process_sp; @@ -454,6 +454,9 @@ llvm::Expected<lldb::addr_t> IRMemoryMap::Malloc(size_t size, uint8_t alignment, (uint64_t)permissions, policy_string, aligned_address); } + if (used_policy) + *used_policy = policy; + return aligned_address; } diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 96add56f92180..17ea1596806d0 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -75,11 +75,12 @@ class EntityPersistentVariable : public Materializer::Entity { // contents. const bool zero_memory = false; + IRMemoryMap::AllocationPolicy used_policy; auto address_or_error = map.Malloc( llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize()) .value_or(0), 8, lldb::ePermissionsReadable | lldb::ePermissionsWritable, - IRMemoryMap::eAllocationPolicyMirror, zero_memory); + IRMemoryMap::eAllocationPolicyMirror, zero_memory, &used_policy); if (!address_or_error) { err = Status::FromErrorStringWithFormat( "couldn't allocate a memory area to store %s: %s", @@ -101,14 +102,22 @@ class EntityPersistentVariable : public Materializer::Entity { m_persistent_variable_sp->GetName(), mem, eAddressTypeLoad, map.GetAddressByteSize()); - // Clear the flag if the variable will never be deallocated. - if (m_persistent_variable_sp->m_flags & ExpressionVariable::EVKeepInTarget) { - Status leak_error; - map.Leak(mem, leak_error); - m_persistent_variable_sp->m_flags &= - ~ExpressionVariable::EVNeedsAllocation; + if (used_policy == IRMemoryMap::eAllocationPolicyMirror) { + // Clear the flag if the variable will never be deallocated. + Status leak_error; + map.Leak(mem, leak_error); + m_persistent_variable_sp->m_flags &= + ~ExpressionVariable::EVNeedsAllocation; + } else { + // If the variable cannot be kept in target, clear this flag... + m_persistent_variable_sp->m_flags &= + ~ExpressionVariable::EVKeepInTarget; + // ...and set the flag to copy the value during dematerialization. + m_persistent_variable_sp->m_flags |= + ExpressionVariable::EVNeedsFreezeDry; + } } // Write the contents of the variable to the area. @@ -327,22 +336,10 @@ class EntityPersistentVariable : public Materializer::Entity { return; } - lldb::ProcessSP process_sp = - map.GetBestExecutionContextScope()->CalculateProcess(); - if (!process_sp || !process_sp->CanJIT()) { - // Allocations are not persistent so persistent variables cannot stay - // materialized. - - m_persistent_variable_sp->m_flags |= - ExpressionVariable::EVNeedsAllocation; - - DestroyAllocation(map, err); - if (!err.Success()) - return; - } else if (m_persistent_variable_sp->m_flags & - ExpressionVariable::EVNeedsAllocation && - !(m_persistent_variable_sp->m_flags & - ExpressionVariable::EVKeepInTarget)) { + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVNeedsAllocation && + !(m_persistent_variable_sp->m_flags & + ExpressionVariable::EVKeepInTarget)) { DestroyAllocation(map, err); if (!err.Success()) return; @@ -1082,9 +1079,8 @@ class EntityResultVariable : public Materializer::Entity { m_delegate->DidDematerialize(ret); } - bool can_persist = - (m_is_program_reference && process_sp && process_sp->CanJIT() && - !(address >= frame_bottom && address < frame_top)); + bool can_persist = m_is_program_reference && + !(address >= frame_bottom && address < frame_top); if (can_persist && m_keep_in_memory) { ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, m_type, name, @@ -1114,7 +1110,9 @@ class EntityResultVariable : public Materializer::Entity { map.Free(m_temporary_allocation, free_error); } } else { - ret->m_flags |= ExpressionVariable::EVIsLLDBAllocated; + ret->m_flags |= m_is_program_reference + ? ExpressionVariable::EVIsProgramReference + : ExpressionVariable::EVIsLLDBAllocated; } m_temporary_allocation = LLDB_INVALID_ADDRESS; diff --git a/lldb/test/API/functionalities/postmortem/elf-core/expr/TestExpr.py b/lldb/test/API/functionalities/postmortem/elf-core/expr/TestExpr.py new file mode 100644 index 0000000000000..dd03a0cc836a7 --- /dev/null +++ b/lldb/test/API/functionalities/postmortem/elf-core/expr/TestExpr.py @@ -0,0 +1,52 @@ +""" +Test evaluating expressions when debugging core file. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +@skipIfLLVMTargetMissing("X86") +class CoreExprTestCase(TestBase): + def setUp(self): + TestBase.setUp(self) + self.target = self.dbg.CreateTarget("linux-x86_64.out") + self.process = self.target.LoadCore("linux-x86_64.core") + self.assertTrue(self.process, PROCESS_IS_VALID) + + def test_result_var(self): + """Test that the result variable can be used in subsequent expressions.""" + + self.expect_expr( + "outer", + result_type="Outer", + result_children=[ValueCheck(name="inner", type="Inner")], + ) + self.expect_expr( + "$0.inner", + result_type="Inner", + result_children=[ValueCheck(name="val", type="int", value="5")], + ) + self.expect_expr("$1.val", result_type="int", result_value="5") + + def test_persist_var(self): + """Test that user-defined variables can be used in subsequent expressions.""" + + self.target.EvaluateExpression("int $my_int = 5") + self.expect_expr("$my_int * 2", result_type="int", result_value="10") + + def test_context_object(self): + """Test expression evaluation in context of an object.""" + + val_outer = self.expect_expr("outer", result_type="Outer") + + val_inner = val_outer.EvaluateExpression("inner") + self.assertTrue(val_inner.IsValid()) + self.assertEqual("Inner", val_inner.GetDisplayTypeName()) + + val_val = val_inner.EvaluateExpression("this->val") + self.assertTrue(val_val.IsValid()) + self.assertEqual("int", val_val.GetDisplayTypeName()) + self.assertEqual(val_val.GetValueAsSigned(), 5) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.core b/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.core new file mode 100644 index 0000000000000..3bd2916c64e9f Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.core diff er diff --git a/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.out b/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.out new file mode 100755 index 0000000000000..33e60f025d210 Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/expr/linux-x86_64.out diff er diff --git a/lldb/test/API/functionalities/postmortem/elf-core/expr/main.cpp b/lldb/test/API/functionalities/postmortem/elf-core/expr/main.cpp new file mode 100644 index 0000000000000..32a9c63e9b716 --- /dev/null +++ b/lldb/test/API/functionalities/postmortem/elf-core/expr/main.cpp @@ -0,0 +1,15 @@ +struct Inner { + Inner(int val) : val(val) {} + int val; +}; + +struct Outer { + Outer(int val) : inner(val) {} + Inner inner; +}; + +extern "C" void _start(void) { + Outer outer(5); + char *boom = (char *)0; + *boom = 47; +} diff --git a/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py b/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py index 4eb5351eefc82..08f09b317b217 100644 --- a/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py +++ b/lldb/test/API/lang/cpp/char8_t/TestCxxChar8_t.py @@ -24,9 +24,7 @@ def test_without_process(self): self.expect_expr("a", result_type="char8_t", result_summary="0x61 u8'a'") self.expect_expr("ab", result_type="const char8_t *", result_summary='u8"你好"') - - # FIXME: This should work too. - self.expect("expr abc", substrs=['u8"你好"'], matching=False) + self.expect_expr("abc", result_type="char8_t[9]", result_summary='u8"你好"') @skipIf(compiler="clang", compiler_version=["<", "7.0"]) def test_with_process(self): _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits