aboutsummaryrefslogtreecommitdiff
path: root/lldb/unittests/Target/MemoryTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/unittests/Target/MemoryTest.cpp')
-rw-r--r--lldb/unittests/Target/MemoryTest.cpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp
index 4a96730..e444f68 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -17,6 +17,7 @@
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "gtest/gtest.h"
+#include <cstdint>
using namespace lldb_private;
using namespace lldb;
@@ -225,3 +226,144 @@ TEST_F(MemoryTest, TesetMemoryCacheRead) {
// instead of using an
// old cache
}
+
+/// A process class that, when asked to read memory from some address X, returns
+/// the least significant byte of X.
+class DummyReaderProcess : public Process {
+public:
+ // If true, `DoReadMemory` will not return all requested bytes.
+ // It's not possible to control exactly how many bytes will be read, because
+ // Process::ReadMemoryFromInferior tries to fulfill the entire request by
+ // reading smaller chunks until it gets nothing back.
+ bool read_less_than_requested = false;
+ bool read_more_than_requested = false;
+
+ size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) override {
+ if (read_less_than_requested && size > 0)
+ size--;
+ if (read_more_than_requested)
+ size *= 2;
+ uint8_t *buffer = static_cast<uint8_t *>(buf);
+ for (lldb::addr_t addr = vm_addr; addr < vm_addr + size; addr++)
+ buffer[addr - vm_addr] = static_cast<uint8_t>(addr); // LSB of addr.
+ return size;
+ }
+ // Boilerplate, nothing interesting below.
+ DummyReaderProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
+ : Process(target_sp, listener_sp) {}
+ bool CanDebug(lldb::TargetSP, bool) override { return true; }
+ Status DoDestroy() override { return {}; }
+ void RefreshStateAfterStop() override {}
+ bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }
+ llvm::StringRef GetPluginName() override { return "Dummy"; }
+};
+
+TEST_F(MemoryTest, TestReadMemoryRanges) {
+ ArchSpec arch("x86_64-apple-macosx-");
+
+ Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
+
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+
+ TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+
+ ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ ProcessSP process_sp =
+ std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
+ ASSERT_TRUE(process_sp);
+
+ {
+ llvm::SmallVector<uint8_t, 0> buffer(1024, 0);
+ // Read 8 ranges of 128 bytes with arbitrary base addresses.
+ llvm::SmallVector<Range<addr_t, size_t>> ranges = {
+ {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128},
+ {0xffaabbccdd, 128}, {0x0, 128}, {0x4242424242, 128},
+ {0x17171717, 128}, {0x99999, 128}};
+
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results =
+ process_sp->ReadMemoryRanges(ranges, buffer);
+
+ for (auto [range, memory] : llvm::zip(ranges, read_results)) {
+ ASSERT_EQ(memory.size(), 128u);
+ addr_t range_base = range.GetRangeBase();
+ for (auto [idx, byte] : llvm::enumerate(memory))
+ ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx));
+ }
+ }
+
+ auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp);
+ dummy_process.read_less_than_requested = true;
+ {
+ llvm::SmallVector<uint8_t, 0> buffer(1024, 0);
+ llvm::SmallVector<Range<addr_t, size_t>> ranges = {
+ {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128}};
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results =
+ dummy_process.ReadMemoryRanges(ranges, buffer);
+ for (auto [range, memory] : llvm::zip(ranges, read_results)) {
+ ASSERT_LT(memory.size(), 128u);
+ addr_t range_base = range.GetRangeBase();
+ for (auto [idx, byte] : llvm::enumerate(memory))
+ ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx));
+ }
+ }
+}
+
+using MemoryDeathTest = MemoryTest;
+
+TEST_F(MemoryDeathTest, TestReadMemoryRangesReturnsTooMuch) {
+ ArchSpec arch("x86_64-apple-macosx-");
+ Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+ TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+ ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ ProcessSP process_sp =
+ std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
+ ASSERT_TRUE(process_sp);
+
+ auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp);
+ dummy_process.read_more_than_requested = true;
+ llvm::SmallVector<uint8_t, 0> buffer(1024, 0);
+ llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128}};
+
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
+ ASSERT_DEBUG_DEATH(
+ { read_results = process_sp->ReadMemoryRanges(ranges, buffer); },
+ "read more than requested bytes");
+#ifdef NDEBUG
+ // With asserts off, the read should return empty ranges.
+ ASSERT_EQ(read_results.size(), 1u);
+ ASSERT_TRUE(read_results[0].empty());
+#endif
+}
+
+TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) {
+ ArchSpec arch("x86_64-apple-macosx-");
+ Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+ TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+ ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ ProcessSP process_sp =
+ std::make_shared<DummyReaderProcess>(target_sp, listener_sp);
+ ASSERT_TRUE(process_sp);
+
+ llvm::SmallVector<uint8_t, 0> short_buffer(10, 0);
+ llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128},
+ {0x11, 128}};
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
+ ASSERT_DEBUG_DEATH(
+ { read_results = process_sp->ReadMemoryRanges(ranges, short_buffer); },
+ "provided buffer is too short");
+#ifdef NDEBUG
+ // With asserts off, the read should return empty ranges.
+ ASSERT_EQ(read_results.size(), ranges.size());
+ for (llvm::MutableArrayRef<uint8_t> result : read_results)
+ ASSERT_TRUE(result.empty());
+#endif
+}