[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
This commit is contained in:
Miro Bucko
2024-06-24 18:51:12 -04:00
committed by GitHub
parent 32e4906c28
commit 0d4da0df16
11 changed files with 697 additions and 6 deletions

View File

@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,40 @@
#include <cstring>
#include <memory>
#include <string>
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;
}