jarin updated this revision to Diff 256207.
jarin marked 10 inline comments as done.
jarin added a comment.

Addressed some of the reviewer comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77765/new/

https://reviews.llvm.org/D77765

Files:
  lldb/include/lldb/Target/Memory.h
  lldb/include/lldb/Target/Process.h
  lldb/source/Target/Memory.cpp
  lldb/source/Target/Process.cpp
  lldb/unittests/Target/CMakeLists.txt
  lldb/unittests/Target/MemoryCacheTest.cpp

Index: lldb/unittests/Target/MemoryCacheTest.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Target/MemoryCacheTest.cpp
@@ -0,0 +1,128 @@
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "lldb/Target/Memory.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+namespace {
+
+class MemoryCacheTest : public MemoryFromInferiorReader, public testing::Test {
+  static const size_t k_memory_size = 256;
+  static const uint64_t k_cache_line_size = 16;
+
+  size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf, size_t size,
+                                Status &error) override {
+    m_inferior_read_count++;
+
+    if (vm_addr >= m_memory.size())
+      return 0;
+
+    size = std::min(size, m_memory.size() - vm_addr);
+    memcpy(buf, m_memory.data() + vm_addr, size);
+    return size;
+  }
+
+public:
+  MemoryCacheTest()
+      : m_cache(*this, k_cache_line_size), m_inferior_read_count(0) {
+    // Fill memory from [0x0 - 0x256) with byte values that match the index. We
+    // will use this memory in each test and each test will start with a fresh
+    // copy.
+    for (size_t i = 0; i < k_memory_size; i++)
+      m_memory.push_back(static_cast<uint8_t>(i));
+  }
+
+  void AddRangeToL1Cache(lldb::addr_t addr, size_t len) {
+    m_cache.AddL1CacheData(addr, m_memory.data() + addr, len);
+  }
+
+  void IncrementAndFlushRange(lldb::addr_t addr, size_t len) {
+    for (size_t i = 0; i < len; i++)
+      m_memory[addr + i]++;
+    m_cache.Flush(addr, len);
+  }
+
+  uint8_t ReadByte(lldb::addr_t addr) {
+    uint8_t result;
+    Status error;
+    m_cache.Read(addr, &result, 1, error);
+    EXPECT_TRUE(error.Success());
+    return result;
+  }
+
+protected:
+  MemoryCache m_cache;
+  std::vector<uint8_t> m_memory;
+  size_t m_inferior_read_count;
+};
+
+TEST_F(MemoryCacheTest, L1FlushSimple) {
+  // Add a byte to the cache.
+  AddRangeToL1Cache(10, 1);
+
+  // Sanity check - memory should agree with what ReadByte returns.
+  EXPECT_EQ(10, m_memory[10]);
+  EXPECT_EQ(m_memory[10], ReadByte(10));
+  // Check that we have hit the cache and not the inferior's memory.
+  EXPECT_EQ(0u, m_inferior_read_count);
+
+  IncrementAndFlushRange(10, 1);
+
+  // Check the memory is incremented and the real memory agrees with ReadByte's
+  // result.
+  EXPECT_EQ(11, m_memory[11]);
+  EXPECT_EQ(m_memory[10], ReadByte(10));
+}
+
+// Let us do a parametrized test that caches two ranges. In the test, we then
+// try to modify (and flush) various regions of memory and check that the cache
+// still returns the correct values from reads.
+using AddrRange = Range<lldb::addr_t, lldb::addr_t>;
+class MemoryCacheParametrizedTest
+    : public MemoryCacheTest,
+      public testing::WithParamInterface<AddrRange> {};
+
+TEST_P(MemoryCacheParametrizedTest, L1FlushWithTwoRegions) {
+  // Add two ranges to the cache.
+  std::vector<AddrRange> cached = {{10, 10}, {30, 10}};
+  for (AddrRange cached_range : cached)
+    AddRangeToL1Cache(cached_range.base, cached_range.size);
+
+  // Flush the range given by the parameter.
+  AddrRange flush_range = GetParam();
+  IncrementAndFlushRange(flush_range.base, flush_range.size);
+
+  // Check that reads from the cached ranges read the inferior's memory
+  // if and only if the flush range intersects with one of the cached ranges.
+  bool has_intersection = false;
+  for (AddrRange cached_range : cached) {
+    if (flush_range.DoesIntersect(cached_range))
+      has_intersection = true;
+
+    for (size_t i = 0; i < cached_range.size; i++)
+      ReadByte(cached_range.base + i);
+  }
+  EXPECT_EQ(has_intersection, m_inferior_read_count > 0);
+
+  // Sanity check: check the memory contents.
+  for (size_t i = 0; i < 50; i++) {
+    EXPECT_EQ(m_memory[i], ReadByte(i)) << " (element at addr=" << i << ")";
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(
+    AllMemoryCacheParametrizedTest, MemoryCacheParametrizedTest,
+    ::testing::Values(AddrRange(5, 6), AddrRange(5, 10), AddrRange(5, 20),
+                      AddrRange(5, 30), AddrRange(5, 40), AddrRange(10, 1),
+                      AddrRange(10, 21), AddrRange(19, 1), AddrRange(19, 11),
+                      AddrRange(19, 12), AddrRange(20, 11), AddrRange(20, 25),
+                      AddrRange(29, 2), AddrRange(29, 12), AddrRange(30, 1),
+                      AddrRange(39, 1), AddrRange(39, 5), AddrRange(5, 1),
+                      AddrRange(5, 5), AddrRange(9, 1), AddrRange(20, 1),
+                      AddrRange(20, 10), AddrRange(29, 1), AddrRange(40, 1),
+                      AddrRange(40, 10)));
+
+} // namespace
Index: lldb/unittests/Target/CMakeLists.txt
===================================================================
--- lldb/unittests/Target/CMakeLists.txt
+++ lldb/unittests/Target/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(TargetTests
   ABITest.cpp
   ExecutionContextTest.cpp
+  MemoryCacheTest.cpp
   MemoryRegionInfoTest.cpp
   ModuleCacheTest.cpp
   PathMappingListTest.cpp
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -485,9 +485,10 @@
       m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
       m_stdin_forward(false), m_stdout_data(), m_stderr_data(),
       m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0),
-      m_memory_cache(*this), m_allocated_memory_cache(*this),
-      m_should_detach(false), m_next_event_action_up(), m_public_run_lock(),
-      m_private_run_lock(), m_finalizing(false), m_finalize_called(false),
+      m_memory_cache(*this, GetMemoryCacheLineSize()),
+      m_allocated_memory_cache(*this), m_should_detach(false),
+      m_next_event_action_up(), m_public_run_lock(), m_private_run_lock(),
+      m_finalizing(false), m_finalize_called(false),
       m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
       m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
       m_can_interpret_function_calls(false), m_warnings_issued(),
@@ -608,7 +609,7 @@
   std::vector<Notifications> empty_notifications;
   m_notifications.swap(empty_notifications);
   m_image_tokens.clear();
-  m_memory_cache.Clear();
+  m_memory_cache.Clear(GetMemoryCacheLineSize());
   m_allocated_memory_cache.Clear();
   {
     std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
@@ -1452,7 +1453,7 @@
       m_mod_id.BumpStopID();
       if (!m_mod_id.IsLastResumeForUserExpression())
         m_mod_id.SetStopEventForLastNaturalStopID(event_sp);
-      m_memory_cache.Clear();
+      m_memory_cache.Clear(GetMemoryCacheLineSize());
       LLDB_LOGF(log, "Process::SetPrivateState (%s) stop_id = %u",
                 StateAsCString(new_state), m_mod_id.GetStopID());
     }
@@ -5571,7 +5572,7 @@
   }
   m_instrumentation_runtimes.clear();
   m_thread_list.DiscardThreadPlans();
-  m_memory_cache.Clear(true);
+  m_memory_cache.Clear(GetMemoryCacheLineSize(), true);
   DoDidExec();
   CompleteAttach();
   // Flush the process (threads and all stack frames) after running
Index: lldb/source/Target/Memory.cpp
===================================================================
--- lldb/source/Target/Memory.cpp
+++ lldb/source/Target/Memory.cpp
@@ -20,21 +20,21 @@
 using namespace lldb_private;
 
 // MemoryCache constructor
-MemoryCache::MemoryCache(Process &process)
+MemoryCache::MemoryCache(MemoryFromInferiorReader &reader,
+                         uint64_t cache_line_size)
     : m_mutex(), m_L1_cache(), m_L2_cache(), m_invalid_ranges(),
-      m_process(process),
-      m_L2_cache_line_byte_size(process.GetMemoryCacheLineSize()) {}
+      m_reader(reader), m_L2_cache_line_byte_size(cache_line_size) {}
 
 // Destructor
 MemoryCache::~MemoryCache() {}
 
-void MemoryCache::Clear(bool clear_invalid_ranges) {
+void MemoryCache::Clear(uint64_t cache_line_size, bool clear_invalid_ranges) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
   m_L1_cache.clear();
   m_L2_cache.clear();
   if (clear_invalid_ranges)
     m_invalid_ranges.Clear();
-  m_L2_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+  m_L2_cache_line_byte_size = cache_line_size;
 }
 
 void MemoryCache::AddL1CacheData(lldb::addr_t addr, const void *src,
@@ -60,7 +60,13 @@
     AddrRange flush_range(addr, size);
     BlockMap::iterator pos = m_L1_cache.upper_bound(addr);
     if (pos != m_L1_cache.begin()) {
-      --pos;
+      // If we are not in the beginning, the previous range might be
+      // intersecting.
+      BlockMap::iterator previous = pos;
+      previous--;
+      AddrRange chunk_range(previous->first, previous->second->GetByteSize());
+      if (chunk_range.DoesIntersect(flush_range))
+        m_L1_cache.erase(previous);
     }
     while (pos != m_L1_cache.end()) {
       AddrRange chunk_range(pos->first, pos->second->GetByteSize());
@@ -157,7 +163,7 @@
   // it in the cache.
   if (dst && dst_len > m_L2_cache_line_byte_size) {
     size_t bytes_read =
-        m_process.ReadMemoryFromInferior(addr, dst, dst_len, error);
+        m_reader.ReadMemoryFromInferior(addr, dst, dst_len, error);
     // Add this non block sized range to the L1 cache if we actually read
     // anything
     if (bytes_read > 0)
@@ -220,24 +226,24 @@
         }
       }
 
-      // We need to read from the process
+      // We need to read from the inferior
 
       if (bytes_left > 0) {
         assert((curr_addr % cache_line_byte_size) == 0);
         std::unique_ptr<DataBufferHeap> data_buffer_heap_up(
             new DataBufferHeap(cache_line_byte_size, 0));
-        size_t process_bytes_read = m_process.ReadMemoryFromInferior(
+        size_t inferior_bytes_read = m_reader.ReadMemoryFromInferior(
             curr_addr, data_buffer_heap_up->GetBytes(),
             data_buffer_heap_up->GetByteSize(), error);
-        if (process_bytes_read == 0)
+        if (inferior_bytes_read == 0)
           return dst_len - bytes_left;
 
-        if (process_bytes_read != cache_line_byte_size) {
-          if (process_bytes_read < data_buffer_heap_up->GetByteSize()) {
-            dst_len -= data_buffer_heap_up->GetByteSize() - process_bytes_read;
-            bytes_left = process_bytes_read;
+        if (inferior_bytes_read != cache_line_byte_size) {
+          if (inferior_bytes_read < data_buffer_heap_up->GetByteSize()) {
+            dst_len -= data_buffer_heap_up->GetByteSize() - inferior_bytes_read;
+            bytes_left = inferior_bytes_read;
           }
-          data_buffer_heap_up->SetByteSize(process_bytes_read);
+          data_buffer_heap_up->SetByteSize(inferior_bytes_read);
         }
         m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release());
         // We have read data and put it into the cache, continue through the
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -355,7 +355,8 @@
                 public UserID,
                 public Broadcaster,
                 public ExecutionContextScope,
-                public PluginInterface {
+                public PluginInterface,
+                public MemoryFromInferiorReader {
   friend class FunctionCaller; // For WaitForStateChangeEventsPrivate
   friend class Debugger; // For PopProcessIOHandler and ProcessIOHandlerIsActive
   friend class DynamicLoader; // For LoadOperatingSystemPlugin
@@ -1480,7 +1481,7 @@
   ///     vm_addr, \a buf, and \a size updated appropriately. Zero is
   ///     returned in the case of an error.
   size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf, size_t size,
-                                Status &error);
+                                Status &error) override;
 
   /// Read a NULL terminated string from memory
   ///
Index: lldb/include/lldb/Target/Memory.h
===================================================================
--- lldb/include/lldb/Target/Memory.h
+++ lldb/include/lldb/Target/Memory.h
@@ -16,16 +16,52 @@
 #include <vector>
 
 namespace lldb_private {
+
+// Abstraction of a reader from inferior's memory.
+class MemoryFromInferiorReader {
+public:
+  /// Read of memory from a process.
+  ///
+  /// This function has the same semantics of ReadMemory except that it
+  /// bypasses caching.
+  ///
+  /// \param[in] vm_addr
+  ///     A virtual load address that indicates where to start reading
+  ///     memory from.
+  ///
+  /// \param[out] buf
+  ///     A byte buffer that is at least \a size bytes long that
+  ///     will receive the memory bytes.
+  ///
+  /// \param[in] size
+  ///     The number of bytes to read.
+  ///
+  /// \param[out] error
+  ///     An error that indicates the success or failure of this
+  ///     operation. If error indicates success (error.Success()),
+  ///     then the value returned can be trusted, otherwise zero
+  ///     will be returned.
+  ///
+  /// \return
+  ///     The number of bytes that were actually read into \a buf. If
+  ///     the returned number is greater than zero, yet less than \a
+  ///     size, then this function will get called again with \a
+  ///     vm_addr, \a buf, and \a size updated appropriately. Zero is
+  ///     returned in the case of an error.
+  virtual size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf,
+                                        size_t size, Status &error) = 0;
+};
+
 // A class to track memory that was read from a live process between
 // runs.
 class MemoryCache {
 public:
   // Constructors and Destructors
-  MemoryCache(Process &process);
+  MemoryCache(MemoryFromInferiorReader &reader, uint64_t cache_line_size);
 
   ~MemoryCache();
 
-  void Clear(bool clear_invalid_ranges = false);
+  void Clear(uint64_t cache_line_size, bool clear_invalid_ranges = false);
 
   void Flush(lldb::addr_t addr, size_t size);
 
@@ -52,10 +88,10 @@
   BlockMap m_L1_cache; // A first level memory cache whose chunk sizes vary that
                        // will be used only if the memory read fits entirely in
                        // a chunk
-  BlockMap m_L2_cache; // A memory cache of fixed size chinks
+  BlockMap m_L2_cache; // A memory cache of fixed size chunks
                        // (m_L2_cache_line_byte_size bytes in size each)
   InvalidRanges m_invalid_ranges;
-  Process &m_process;
+  MemoryFromInferiorReader &m_reader;
   uint32_t m_L2_cache_line_byte_size;
 
 private:
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to