From 0d4da0df166ea7512c6e97e182b21cd706293eaa Mon Sep 17 00:00:00 2001 From: Miro Bucko Date: Mon, 24 Jun 2024 18:51:12 -0400 Subject: [lldb][API] Add Find(Ranges)InMemory() to Process SB API (#96569) This is a second attempt to land #95007 Test Plan: llvm-lit llvm-project/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py llvm-project/lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py Reviewers: clayborg Tasks: lldb --- lldb/test/API/python_api/find_in_memory/Makefile | 3 + .../python_api/find_in_memory/TestFindInMemory.py | 154 ++++++++++++++ .../find_in_memory/TestFindRangesInMemory.py | 221 +++++++++++++++++++++ .../find_in_memory/address_ranges_helper.py | 73 +++++++ lldb/test/API/python_api/find_in_memory/main.cpp | 40 ++++ 5 files changed, 491 insertions(+) create mode 100644 lldb/test/API/python_api/find_in_memory/Makefile create mode 100644 lldb/test/API/python_api/find_in_memory/TestFindInMemory.py create mode 100644 lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py create mode 100644 lldb/test/API/python_api/find_in_memory/address_ranges_helper.py create mode 100644 lldb/test/API/python_api/find_in_memory/main.cpp (limited to 'lldb/test/API/python_api') diff --git a/lldb/test/API/python_api/find_in_memory/Makefile b/lldb/test/API/python_api/find_in_memory/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/python_api/find_in_memory/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py b/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py new file mode 100644 index 0000000..9ab4619 --- /dev/null +++ b/lldb/test/API/python_api/find_in_memory/TestFindInMemory.py @@ -0,0 +1,154 @@ +""" +Test Process::FindInMemory. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from address_ranges_helper import * + + +class FindInMemoryTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + + self.build() + ( + self.target, + self.process, + self.thread, + self.bp, + ) = lldbutil.run_to_source_breakpoint( + self, + "break here", + lldb.SBFileSpec("main.cpp"), + ) + self.assertTrue(self.bp.IsValid()) + + def test_check_stack_pointer(self): + """Make sure the 'stack_pointer' variable lives on the stack""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + frame = self.thread.GetSelectedFrame() + ex = frame.EvaluateExpression("&stack_pointer") + variable_region = lldb.SBMemoryRegionInfo() + self.assertTrue( + self.process.GetMemoryRegionInfo( + ex.GetValueAsUnsigned(), variable_region + ).Success(), + ) + + stack_region = lldb.SBMemoryRegionInfo() + self.assertTrue( + self.process.GetMemoryRegionInfo(frame.GetSP(), stack_region).Success(), + ) + + self.assertEqual(variable_region, stack_region) + + def test_find_in_memory_ok(self): + """Make sure a match exists in the heap memory and the right address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + error = lldb.SBError() + addr = self.process.FindInMemory( + SINGLE_INSTANCE_PATTERN_STACK, + GetStackRange(self), + 1, + error, + ) + + self.assertSuccess(error) + self.assertNotEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + def test_find_in_memory_double_instance_ok(self): + """Make sure a match exists in the heap memory and the right address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + error = lldb.SBError() + addr = self.process.FindInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + GetHeapRanges(self)[0], + 1, + error, + ) + + self.assertSuccess(error) + self.assertNotEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + def test_find_in_memory_invalid_alignment(self): + """Make sure the alignment 0 is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + error = lldb.SBError() + addr = self.process.FindInMemory( + SINGLE_INSTANCE_PATTERN_STACK, + GetStackRange(self), + 0, + error, + ) + + self.assertFailure(error) + self.assertEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + def test_find_in_memory_invalid_address_range(self): + """Make sure invalid address range is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + error = lldb.SBError() + addr = self.process.FindInMemory( + SINGLE_INSTANCE_PATTERN_STACK, + lldb.SBAddressRange(), + 1, + error, + ) + + self.assertFailure(error) + self.assertEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + def test_find_in_memory_invalid_buffer(self): + """Make sure the empty buffer is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + error = lldb.SBError() + addr = self.process.FindInMemory( + "", + GetStackRange(self), + 1, + error, + ) + + self.assertFailure(error) + self.assertEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + def test_find_in_memory_unaligned(self): + """Make sure the unaligned match exists in the heap memory and is not found with alignment 8""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + error = lldb.SBError() + range = GetAlignedRange(self) + + # First we make sure the pattern is found with alignment 1 + addr = self.process.FindInMemory( + UNALIGNED_INSTANCE_PATTERN_HEAP, + range, + 1, + error, + ) + self.assertSuccess(error) + self.assertNotEqual(addr, lldb.LLDB_INVALID_ADDRESS) + + # With alignment 8 the pattern should not be found + addr = self.process.FindInMemory( + UNALIGNED_INSTANCE_PATTERN_HEAP, + range, + 8, + error, + ) + self.assertSuccess(error) + self.assertEqual(addr, lldb.LLDB_INVALID_ADDRESS) diff --git a/lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py b/lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py new file mode 100644 index 0000000..31bc0e9 --- /dev/null +++ b/lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py @@ -0,0 +1,221 @@ +""" +Test Process::FindRangesInMemory. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from address_ranges_helper import * + + +class FindRangesInMemoryTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + + self.build() + ( + self.target, + self.process, + self.thread, + self.bp, + ) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp") + ) + self.assertTrue(self.bp.IsValid()) + + def test_find_ranges_in_memory_two_matches(self): + """Make sure two matches exist in the heap memory and the right address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetHeapRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 10, + error, + ) + + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 2) + + def test_find_ranges_in_memory_one_match(self): + """Make sure exactly one match exists in the heap memory and the right address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetStackRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + SINGLE_INSTANCE_PATTERN_STACK, + addr_ranges, + 1, + 10, + error, + ) + + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 1) + + def test_find_ranges_in_memory_one_match_multiple_ranges(self): + """Make sure exactly one match exists in the heap memory and multiple address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetRanges(self) + addr_ranges.Append(lldb.SBAddressRange()) + self.assertGreater(addr_ranges.GetSize(), 2) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + SINGLE_INSTANCE_PATTERN_STACK, + addr_ranges, + 1, + 10, + error, + ) + + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 1) + + def test_find_ranges_in_memory_one_match_max(self): + """Make sure at least one matche exists in the heap memory and the right address ranges are provided""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetHeapRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 1, + error, + ) + + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 1) + + def test_find_ranges_in_memory_invalid_alignment(self): + """Make sure the alignment 0 is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetHeapRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 0, + 10, + error, + ) + + self.assertFailure(error) + self.assertEqual(matches.GetSize(), 0) + + def test_find_ranges_in_memory_invalid_range(self): + """Make sure the alignment 0 is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = lldb.SBAddressRangeList() + addr_ranges.Append(lldb.SBAddressRange()) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 10, + error, + ) + + self.assertFailure(error) + self.assertIn("unable to resolve any ranges", str(error)) + self.assertEqual(matches.GetSize(), 0) + + def test_find_ranges_in_memory_empty_ranges(self): + """Make sure the empty ranges is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = lldb.SBAddressRangeList() + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 10, + error, + ) + + self.assertFailure(error) + self.assertEqual(matches.GetSize(), 0) + + def test_find_ranges_in_memory_invalid_buffer(self): + """Make sure the empty buffer is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetHeapRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + "", + addr_ranges, + 1, + 10, + error, + ) + + self.assertFailure(error) + self.assertEqual(matches.GetSize(), 0) + + def test_find_ranges_in_memory_invalid_max_matches(self): + """Make sure the empty buffer is failing""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = GetHeapRanges(self) + error = lldb.SBError() + matches = self.process.FindRangesInMemory( + DOUBLE_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 0, + error, + ) + + self.assertFailure(error) + self.assertEqual(matches.GetSize(), 0) + + def test_find_in_memory_unaligned(self): + """Make sure the unaligned match exists in the heap memory and is not found with alignment 8""" + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertState(self.process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + addr_ranges = lldb.SBAddressRangeList() + addr_ranges.Append(GetAlignedRange(self)) + error = lldb.SBError() + + matches = self.process.FindRangesInMemory( + UNALIGNED_INSTANCE_PATTERN_HEAP, + addr_ranges, + 1, + 10, + error, + ) + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 1) + + matches = self.process.FindRangesInMemory( + UNALIGNED_INSTANCE_PATTERN_HEAP, + addr_ranges, + 8, + 10, + error, + ) + self.assertSuccess(error) + self.assertEqual(matches.GetSize(), 0) diff --git a/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py b/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py new file mode 100644 index 0000000..810fb9f --- /dev/null +++ b/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py @@ -0,0 +1,73 @@ +import lldb + +SINGLE_INSTANCE_PATTERN_STACK = "stack_there_is_only_one_of_me" +DOUBLE_INSTANCE_PATTERN_HEAP = "heap_there_is_exactly_two_of_me" +ALIGNED_INSTANCE_PATTERN_HEAP = "i_am_unaligned_string_on_the_heap" +UNALIGNED_INSTANCE_PATTERN_HEAP = ALIGNED_INSTANCE_PATTERN_HEAP[1:] + + +def GetAlignedRange(test_base): + frame = test_base.thread.GetSelectedFrame() + ex = frame.EvaluateExpression("aligned_string_ptr") + test_base.assertTrue(ex.IsValid()) + return GetRangeFromAddrValue(test_base, ex) + + +def GetStackRange(test_base): + frame = test_base.thread.GetSelectedFrame() + ex = frame.EvaluateExpression("&stack_pointer") + test_base.assertTrue(ex.IsValid()) + return GetRangeFromAddrValue(test_base, ex) + + +def GetStackRanges(test_base): + addr_ranges = lldb.SBAddressRangeList() + addr_ranges.Append(GetStackRange(test_base)) + return addr_ranges + + +def GetRangeFromAddrValue(test_base, addr): + region = lldb.SBMemoryRegionInfo() + test_base.assertTrue( + test_base.process.GetMemoryRegionInfo( + addr.GetValueAsUnsigned(), region + ).Success(), + ) + + test_base.assertTrue(region.IsReadable()) + test_base.assertFalse(region.IsExecutable()) + + address_start = lldb.SBAddress(region.GetRegionBase(), test_base.target) + stack_size = region.GetRegionEnd() - region.GetRegionBase() + return lldb.SBAddressRange(address_start, stack_size) + + +def IsWithinRange(addr, range, target): + start_addr = range.GetBaseAddress().GetLoadAddress(target) + end_addr = start_addr + range.GetByteSize() + addr = addr.GetValueAsUnsigned() + return addr >= start_addr and addr < end_addr + + +def GetHeapRanges(test_base): + frame = test_base.thread.GetSelectedFrame() + + ex = frame.EvaluateExpression("heap_pointer1") + test_base.assertTrue(ex.IsValid()) + range = GetRangeFromAddrValue(test_base, ex) + addr_ranges = lldb.SBAddressRangeList() + addr_ranges.Append(range) + + ex = frame.EvaluateExpression("heap_pointer2") + test_base.assertTrue(ex.IsValid()) + if not IsWithinRange(ex, addr_ranges[0], test_base.target): + addr_ranges.Append(GetRangeFromAddrValue(test_base, ex)) + + return addr_ranges + + +def GetRanges(test_base): + ranges = GetHeapRanges(test_base) + ranges.Append(GetStackRanges(test_base)) + + return ranges diff --git a/lldb/test/API/python_api/find_in_memory/main.cpp b/lldb/test/API/python_api/find_in_memory/main.cpp new file mode 100644 index 0000000..13402e3 --- /dev/null +++ b/lldb/test/API/python_api/find_in_memory/main.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +int main() { + // Stack + const char stack_pointer[] = "stack_there_is_only_one_of_me"; + + // Heap + // This test relies on std::string objects with size over 22 characters being + // allocated on the heap. + const std::string heap_string1("heap_there_is_exactly_two_of_me"); + const std::string heap_string2("heap_there_is_exactly_two_of_me"); + const char *heap_pointer1 = heap_string1.data(); + const char *heap_pointer2 = heap_string2.data(); + + // Aligned Heap + constexpr char aligned_string[] = "i_am_unaligned_string_on_the_heap"; + constexpr size_t buffer_size = 100; + constexpr size_t len = sizeof(aligned_string) + 1; + // Allocate memory aligned to 8-byte boundary + void *aligned_string_ptr = new size_t[buffer_size]; + if (aligned_string_ptr == nullptr) { + return -1; + } + // Zero out the memory + memset(aligned_string_ptr, 0, buffer_size); + + // Align the pointer to a multiple of 8 bytes + size_t size = buffer_size; + aligned_string_ptr = std::align(8, len, aligned_string_ptr, size); + + // Copy the string to aligned memory + memcpy(aligned_string_ptr, aligned_string, len); + + (void)stack_pointer; + (void)heap_pointer1; + (void)heap_pointer2; // break here + return 0; +} -- cgit v1.1