[Lldb-commits] [lldb] [lldb][API] Add Find(Ranges)InMemory() to Process SB API (PR #96569)

via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 24 15:36:45 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Miro Bucko (mbucko)

<details>
<summary>Changes</summary>

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

---

Patch is 29.31 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/96569.diff


11 Files Affected:

- (modified) lldb/bindings/python/python-typemaps.swig (+2-1) 
- (modified) lldb/include/lldb/API/SBProcess.h (+10) 
- (modified) lldb/include/lldb/Core/AddressRangeListImpl.h (+4) 
- (modified) lldb/include/lldb/Target/Process.h (+14) 
- (modified) lldb/source/API/SBProcess.cpp (+53-5) 
- (modified) lldb/source/Target/Process.cpp (+123) 
- (added) lldb/test/API/python_api/find_in_memory/Makefile (+3) 
- (added) lldb/test/API/python_api/find_in_memory/TestFindInMemory.py (+154) 
- (added) lldb/test/API/python_api/find_in_memory/TestFindRangesInMemory.py (+221) 
- (added) lldb/test/API/python_api/find_in_memory/address_ranges_helper.py (+73) 
- (added) lldb/test/API/python_api/find_in_memory/main.cpp (+38) 


``````````diff
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index c39594c7df041..f8c33e15c03e6 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -257,7 +257,8 @@ AND call SWIG_fail at the same time, because it will result in a double free.
 }
 // For SBProcess::WriteMemory, SBTarget::GetInstructions and SBDebugger::DispatchInput.
 %typemap(in) (const void *buf, size_t size),
-             (const void *data, size_t data_len) {
+             (const void *data, size_t data_len),
+             (const void *buf, uint64_t size) {
   if (PythonString::Check($input)) {
     PythonString str(PyRefType::Borrowed, $input);
     $1 = (void *)str.GetString().data();
diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h
index f1b5d1fb92ce2..a6ab7ae759918 100644
--- a/lldb/include/lldb/API/SBProcess.h
+++ b/lldb/include/lldb/API/SBProcess.h
@@ -209,6 +209,16 @@ class LLDB_API SBProcess {
 
   lldb::addr_t ReadPointerFromMemory(addr_t addr, lldb::SBError &error);
 
+  lldb::SBAddressRangeList FindRangesInMemory(const void *buf, uint64_t size,
+                                              const SBAddressRangeList &ranges,
+                                              uint32_t alignment,
+                                              uint32_t max_matches,
+                                              SBError &error);
+
+  lldb::addr_t FindInMemory(const void *buf, uint64_t size,
+                            const SBAddressRange &range, uint32_t alignment,
+                            SBError &error);
+
   // Events
   static lldb::StateType GetStateFromEvent(const lldb::SBEvent &event);
 
diff --git a/lldb/include/lldb/Core/AddressRangeListImpl.h b/lldb/include/lldb/Core/AddressRangeListImpl.h
index 46ebfe73d4d92..6742e6ead87de 100644
--- a/lldb/include/lldb/Core/AddressRangeListImpl.h
+++ b/lldb/include/lldb/Core/AddressRangeListImpl.h
@@ -13,7 +13,9 @@
 #include <cstddef>
 
 namespace lldb {
+class SBAddressRangeList;
 class SBBlock;
+class SBProcess;
 }
 
 namespace lldb_private {
@@ -39,7 +41,9 @@ class AddressRangeListImpl {
   lldb_private::AddressRange GetAddressRangeAtIndex(size_t index);
 
 private:
+  friend class lldb::SBAddressRangeList;
   friend class lldb::SBBlock;
+  friend class lldb::SBProcess;
 
   AddressRanges &ref();
 
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index eec337c15f7ed..ceaf547ebddaf 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -2685,6 +2685,15 @@ void PruneThreadPlans();
   lldb::addr_t FindInMemory(lldb::addr_t low, lldb::addr_t high,
                             const uint8_t *buf, size_t size);
 
+  AddressRanges FindRangesInMemory(const uint8_t *buf, uint64_t size,
+                                   const AddressRanges &ranges,
+                                   size_t alignment, size_t max_matches,
+                                   Status &error);
+
+  lldb::addr_t FindInMemory(const uint8_t *buf, uint64_t size,
+                            const AddressRange &range, size_t alignment,
+                            Status &error);
+
 protected:
   friend class Trace;
 
@@ -2800,6 +2809,11 @@ void PruneThreadPlans();
   virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
                               Status &error) = 0;
 
+  virtual void DoFindInMemory(lldb::addr_t start_addr, lldb::addr_t end_addr,
+                              const uint8_t *buf, size_t size,
+                              AddressRanges &matches, size_t alignment,
+                              size_t max_matches);
+
   /// DoGetMemoryRegionInfo is called by GetMemoryRegionInfo after it has
   /// removed non address bits from load_addr. Override this method in
   /// subclasses of Process.
diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp
index c37c111c5a58e..efb3c8def2796 100644
--- a/lldb/source/API/SBProcess.cpp
+++ b/lldb/source/API/SBProcess.cpp
@@ -14,6 +14,7 @@
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-types.h"
 
+#include "lldb/Core/AddressRangeListImpl.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/PluginManager.h"
@@ -26,6 +27,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Utility/Args.h"
+#include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/ProcessInfo.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
@@ -320,8 +322,8 @@ void SBProcess::ReportEventState(const SBEvent &event, FileSP out) const {
   if (process_sp) {
     StreamFile stream(out);
     const StateType event_state = SBProcess::GetStateFromEvent(event);
-    stream.Printf("Process %" PRIu64 " %s\n",
-        process_sp->GetID(), SBDebugger::StateAsCString(event_state));
+    stream.Printf("Process %" PRIu64 " %s\n", process_sp->GetID(),
+                  SBDebugger::StateAsCString(event_state));
   }
 }
 
@@ -378,7 +380,6 @@ bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) {
     ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id);
   }
 
-
   return ret_val;
 }
 
@@ -546,7 +547,6 @@ ByteOrder SBProcess::GetByteOrder() const {
   if (process_sp)
     byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder();
 
-
   return byteOrder;
 }
 
@@ -558,7 +558,6 @@ uint32_t SBProcess::GetAddressByteSize() const {
   if (process_sp)
     size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
 
-
   return size;
 }
 
@@ -810,6 +809,55 @@ const char *SBProcess::GetBroadcasterClass() {
   return ConstString(Process::GetStaticBroadcasterClass()).AsCString();
 }
 
+lldb::SBAddressRangeList SBProcess::FindRangesInMemory(
+    const void *buf, uint64_t size, const SBAddressRangeList &ranges,
+    uint32_t alignment, uint32_t max_matches, SBError &error) {
+  LLDB_INSTRUMENT_VA(this, buf, size, ranges, alignment, max_matches, error);
+
+  lldb::SBAddressRangeList matches;
+
+  ProcessSP process_sp(GetSP());
+  if (!process_sp) {
+    error.SetErrorString("SBProcess is invalid");
+    return matches;
+  }
+  Process::StopLocker stop_locker;
+  if (!stop_locker.TryLock(&process_sp->GetRunLock())) {
+    error.SetErrorString("process is running");
+    return matches;
+  }
+  std::lock_guard<std::recursive_mutex> guard(
+      process_sp->GetTarget().GetAPIMutex());
+  matches.m_opaque_up->ref() = process_sp->FindRangesInMemory(
+      reinterpret_cast<const uint8_t *>(buf), size, ranges.ref().ref(),
+      alignment, max_matches, error.ref());
+  return matches;
+}
+
+lldb::addr_t SBProcess::FindInMemory(const void *buf, uint64_t size,
+                                     const SBAddressRange &range,
+                                     uint32_t alignment, SBError &error) {
+  LLDB_INSTRUMENT_VA(this, buf, size, range, alignment, error);
+
+  ProcessSP process_sp(GetSP());
+
+  if (!process_sp) {
+    error.SetErrorString("SBProcess is invalid");
+    return LLDB_INVALID_ADDRESS;
+  }
+
+  Process::StopLocker stop_locker;
+  if (!stop_locker.TryLock(&process_sp->GetRunLock())) {
+    error.SetErrorString("process is running");
+    return LLDB_INVALID_ADDRESS;
+  }
+
+  std::lock_guard<std::recursive_mutex> guard(
+      process_sp->GetTarget().GetAPIMutex());
+  return process_sp->FindInMemory(reinterpret_cast<const uint8_t *>(buf), size,
+                                  range.ref(), alignment, error.ref());
+}
+
 size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len,
                              SBError &sb_error) {
   LLDB_INSTRUMENT_VA(this, addr, dst, dst_len, sb_error);
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 9b905663a2c32..6fac0df1d7a66 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2007,6 +2007,129 @@ size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
   }
 }
 
+void Process::DoFindInMemory(lldb::addr_t start_addr, lldb::addr_t end_addr,
+                             const uint8_t *buf, size_t size,
+                             AddressRanges &matches, size_t alignment,
+                             size_t max_matches) {
+  // Inputs are already validated in FindInMemory() functions.
+  assert(buf != nullptr);
+  assert(size > 0);
+  assert(alignment > 0);
+  assert(max_matches > 0);
+  assert(start_addr != LLDB_INVALID_ADDRESS);
+  assert(end_addr != LLDB_INVALID_ADDRESS);
+  assert(start_addr < end_addr);
+
+  lldb::addr_t start = llvm::alignTo(start_addr, alignment);
+  while (matches.size() < max_matches && (start + size) < end_addr) {
+    const lldb::addr_t found_addr = FindInMemory(start, end_addr, buf, size);
+    if (found_addr == LLDB_INVALID_ADDRESS)
+      break;
+
+    if (found_addr % alignment) {
+      // We need to check the alignment because the FindInMemory uses a special
+      // algorithm to efficiently search mememory but doesn't support alignment.
+      start = llvm::alignTo(start + 1, alignment);
+      continue;
+    }
+
+    matches.emplace_back(found_addr, size);
+    start = found_addr + alignment;
+  }
+}
+
+AddressRanges Process::FindRangesInMemory(const uint8_t *buf, uint64_t size,
+                                          const AddressRanges &ranges,
+                                          size_t alignment, size_t max_matches,
+                                          Status &error) {
+  AddressRanges matches;
+  if (buf == nullptr) {
+    error.SetErrorString("buffer is null");
+    return matches;
+  }
+  if (size == 0) {
+    error.SetErrorString("buffer size is zero");
+    return matches;
+  }
+  if (ranges.empty()) {
+    error.SetErrorString("empty ranges");
+    return matches;
+  }
+  if (alignment == 0) {
+    error.SetErrorString("alignment must be greater than zero");
+    return matches;
+  }
+  if (max_matches == 0) {
+    error.SetErrorString("max_matches must be greater than zero");
+    return matches;
+  }
+
+  int resolved_ranges = 0;
+  Target &target = GetTarget();
+  for (size_t i = 0; i < ranges.size(); ++i) {
+    if (matches.size() >= max_matches)
+      break;
+    const AddressRange &range = ranges[i];
+    if (range.IsValid() == false)
+      continue;
+
+    const lldb::addr_t start_addr =
+        range.GetBaseAddress().GetLoadAddress(&target);
+    if (start_addr == LLDB_INVALID_ADDRESS)
+      continue;
+
+    ++resolved_ranges;
+    const lldb::addr_t end_addr = start_addr + range.GetByteSize();
+    DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment,
+                   max_matches);
+  }
+
+  if (resolved_ranges > 0)
+    error.Clear();
+  else
+    error.SetErrorString("unable to resolve any ranges");
+
+  return matches;
+}
+
+lldb::addr_t Process::FindInMemory(const uint8_t *buf, uint64_t size,
+                                   const AddressRange &range, size_t alignment,
+                                   Status &error) {
+  if (buf == nullptr) {
+    error.SetErrorString("buffer is null");
+    return LLDB_INVALID_ADDRESS;
+  }
+  if (size == 0) {
+    error.SetErrorString("buffer size is zero");
+    return LLDB_INVALID_ADDRESS;
+  }
+  if (!range.IsValid()) {
+    error.SetErrorString("range is invalid");
+    return LLDB_INVALID_ADDRESS;
+  }
+  if (alignment == 0) {
+    error.SetErrorString("alignment must be greater than zero");
+    return LLDB_INVALID_ADDRESS;
+  }
+
+  Target &target = GetTarget();
+  const lldb::addr_t start_addr =
+      range.GetBaseAddress().GetLoadAddress(&target);
+  if (start_addr == LLDB_INVALID_ADDRESS) {
+    error.SetErrorString("range load address is invalid");
+    return LLDB_INVALID_ADDRESS;
+  }
+  const lldb::addr_t end_addr = start_addr + range.GetByteSize();
+
+  AddressRanges matches;
+  DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment, 1);
+  if (matches.empty())
+    return LLDB_INVALID_ADDRESS;
+
+  error.Clear();
+  return matches[0].GetBaseAddress().GetLoadAddress(&target);
+}
+
 size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str,
                                       Status &error) {
   char buf[256];
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 0000000000000..99998b20bcb05
--- /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 0000000000000..9ab4619b1f8f4
--- /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 0000000000000..31bc0e99f4914
--- /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 memor...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/96569


More information about the lldb-commits mailing list