diff --git a/lldb/include/lldb/Target/Memory.h b/lldb/include/lldb/Target/Memory.h index 85584f29ec7e7..2b8655e277a29 100644 --- a/lldb/include/lldb/Target/Memory.h +++ b/lldb/include/lldb/Target/Memory.h @@ -11,6 +11,8 @@ #include "lldb/Utility/RangeMap.h" #include "lldb/lldb-private.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include #include #include @@ -31,6 +33,13 @@ class MemoryCache { size_t Read(lldb::addr_t addr, void *dst, size_t dst_len, Status &error); + /// Reads multiple memory ranges, serving cache hits from L1 and batching all + /// misses through Process::DoReadMemoryRanges. The semantics of the return + /// value match Process::ReadMemoryRanges. + llvm::SmallVector> + ReadRanges(llvm::ArrayRef> ranges, + llvm::MutableArrayRef buffer); + uint32_t GetMemoryCacheLineSize() const { return m_L2_cache_line_byte_size; } void AddInvalidRange(lldb::addr_t base_addr, lldb::addr_t byte_size); @@ -40,6 +49,11 @@ class MemoryCache { // Allow external sources to populate data into the L1 memory cache void AddL1CacheData(lldb::addr_t addr, const void *src, size_t src_len); + void AddL1CacheData(lldb::addr_t addr, llvm::ArrayRef src) { + if (!src.empty()) + AddL1CacheData(addr, src.data(), src.size()); + } + void AddL1CacheData(lldb::addr_t addr, const lldb::DataBufferSP &data_buffer_sp); diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index f68ea3b639e93..8432c326d3281 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -362,6 +362,7 @@ class Process : public std::enable_shared_from_this, friend class StopInfo; friend class Target; friend class ThreadList; + friend class MemoryCache; public: /// Broadcaster event bits definitions. diff --git a/lldb/source/Target/Memory.cpp b/lldb/source/Target/Memory.cpp index 6c4650f1eb2d7..3684d9410b64a 100644 --- a/lldb/source/Target/Memory.cpp +++ b/lldb/source/Target/Memory.cpp @@ -14,6 +14,8 @@ #include "lldb/Utility/RangeMap.h" #include "lldb/Utility/State.h" +#include "llvm/ADT/STLExtras.h" + #include #include @@ -270,6 +272,55 @@ size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len, return dst_len; } +llvm::SmallVector> +MemoryCache::ReadRanges(llvm::ArrayRef> ranges, + llvm::MutableArrayRef buffer) { + std::lock_guard guard(m_mutex); + + llvm::SmallVector> results; + results.reserve(ranges.size()); + llvm::SmallVector> missed_ranges; + + // Iterate once serving requests from L1. + for (auto range : ranges) { + const lldb::addr_t addr = range.GetRangeBase(); + const size_t len = range.GetByteSize(); + + if (m_invalid_ranges.FindEntryThatContains(addr)) { + results.push_back(buffer.take_front(0)); + continue; + } + + if (const uint8_t *l1_data = FindL1CacheEntry(addr, len)) { + results.push_back(buffer.take_front(len)); + buffer = buffer.drop_front(len); + memcpy(results.back().data(), l1_data, len); + continue; + } + + // Use a nullptr to denote this needs fetching. + results.emplace_back(nullptr, nullptr); + missed_ranges.push_back(range); + } + + if (missed_ranges.empty()) + return results; + + llvm::SmallVector> fetched_buffers_vec = + m_process.DoReadMemoryRanges(missed_ranges, buffer); + auto fetched_buffers = llvm::ArrayRef(fetched_buffers_vec); + + for (auto [missed_range, fetched] : llvm::zip(missed_ranges, fetched_buffers)) + AddL1CacheData(missed_range.GetRangeBase(), fetched); + + // Use the just-fetched memory to fill in the gaps left by the cache. + for (auto &result : results) + if (result.data() == nullptr) + result = fetched_buffers.consume_front(); + + return results; +} + AllocatedBlock::AllocatedBlock(lldb::addr_t addr, uint32_t byte_size, uint32_t permissions, uint32_t chunk_size) : m_range(addr, byte_size), m_permissions(permissions), diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e77cb0b0835e1..ac1357f7d00a1 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2077,6 +2077,8 @@ Process::ReadMemoryRanges(llvm::ArrayRef> ranges, for (const Range &range : ranges) fixed_ranges.emplace_back(FixAnyAddress(range.GetRangeBase()), range.GetByteSize()); + if (!GetDisableMemoryCache()) + return m_memory_cache.ReadRanges(fixed_ranges, buffer); return DoReadMemoryRanges(fixed_ranges, buffer); } diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp index e2a125e7e10a9..9d04376b4fd5b 100644 --- a/lldb/unittests/Target/MemoryTest.cpp +++ b/lldb/unittests/Target/MemoryTest.cpp @@ -407,7 +407,7 @@ TEST_F(MemoryTest, TestReadMemoryRanges) { { llvm::SmallVector buffer(1024, 0); llvm::SmallVector> ranges = { - {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128}}; + {0x6789, 128}, {0x333344444, 128}, {0x99999999, 128}}; llvm::SmallVector> read_results = dummy_process.ReadMemoryRanges(ranges, buffer); for (auto [range, memory] : llvm::zip(ranges, read_results)) {