437 lines · cpp
1//===-- MemoryTest.cpp ----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Target/Memory.h"10#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"11#include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"12#include "lldb/Core/Debugger.h"13#include "lldb/Host/FileSystem.h"14#include "lldb/Host/HostInfo.h"15#include "lldb/Target/Process.h"16#include "lldb/Target/Target.h"17#include "lldb/Utility/ArchSpec.h"18#include "lldb/Utility/DataBufferHeap.h"19#include "gtest/gtest.h"20#include <cstdint>21 22using namespace lldb_private;23using namespace lldb;24 25namespace {26class MemoryTest : public ::testing::Test {27public:28 void SetUp() override {29 FileSystem::Initialize();30 HostInfo::Initialize();31 PlatformMacOSX::Initialize();32 }33 void TearDown() override {34 PlatformMacOSX::Terminate();35 HostInfo::Terminate();36 FileSystem::Terminate();37 }38};39 40class DummyProcess : public Process {41public:42 DummyProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)43 : Process(target_sp, listener_sp), m_bytes_left(0) {}44 45 // Required overrides46 bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {47 return true;48 }49 Status DoDestroy() override { return {}; }50 void RefreshStateAfterStop() override {}51 // Required by Target::ReadMemory() to call Process::ReadMemory()52 bool IsAlive() override { return true; }53 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,54 Status &error) override {55 if (m_bytes_left == 0)56 return 0;57 58 size_t num_bytes_to_write = size;59 if (m_bytes_left < size) {60 num_bytes_to_write = m_bytes_left;61 m_bytes_left = 0;62 } else {63 m_bytes_left -= size;64 }65 66 memset(buf, m_filler, num_bytes_to_write);67 return num_bytes_to_write;68 }69 bool DoUpdateThreadList(ThreadList &old_thread_list,70 ThreadList &new_thread_list) override {71 return false;72 }73 llvm::StringRef GetPluginName() override { return "Dummy"; }74 75 // Test-specific additions76 size_t m_bytes_left;77 int m_filler = 'B';78 MemoryCache &GetMemoryCache() { return m_memory_cache; }79 void SetMaxReadSize(size_t size) { m_bytes_left = size; }80 void SetFiller(int filler) { m_filler = filler; }81};82} // namespace83 84TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) {85 PlatformSP platform_sp;86 TargetSP target_sp;87 debugger_sp->GetTargetList().CreateTarget(88 *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);89 return target_sp;90}91 92static ProcessSP CreateProcess(lldb::TargetSP target_sp) {93 ListenerSP listener_sp(Listener::MakeListener("dummy"));94 ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);95 96 struct TargetHack : public Target {97 void SetProcess(ProcessSP process) { m_process_sp = process; }98 };99 static_cast<TargetHack *>(target_sp.get())->SetProcess(process_sp);100 101 return process_sp;102}103 104TEST_F(MemoryTest, TesetMemoryCacheRead) {105 ArchSpec arch("x86_64-apple-macosx-");106 107 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));108 109 DebuggerSP debugger_sp = Debugger::CreateInstance();110 ASSERT_TRUE(debugger_sp);111 112 TargetSP target_sp = CreateTarget(debugger_sp, arch);113 ASSERT_TRUE(target_sp);114 115 ProcessSP process_sp = CreateProcess(target_sp);116 ASSERT_TRUE(process_sp);117 118 DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());119 MemoryCache &mem_cache = process->GetMemoryCache();120 const uint64_t l2_cache_size = process->GetMemoryCacheLineSize();121 Status error;122 auto data_sp = std::make_shared<DataBufferHeap>(l2_cache_size * 2, '\0');123 size_t bytes_read = 0;124 125 // Cache empty, memory read fails, size > l2 cache size126 process->SetMaxReadSize(0);127 bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),128 data_sp->GetByteSize(), error);129 ASSERT_TRUE(bytes_read == 0);130 131 // Cache empty, memory read fails, size <= l2 cache size132 data_sp->SetByteSize(l2_cache_size);133 bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),134 data_sp->GetByteSize(), error);135 ASSERT_TRUE(bytes_read == 0);136 137 // Cache empty, memory read succeeds, size > l2 cache size138 process->SetMaxReadSize(l2_cache_size * 4);139 data_sp->SetByteSize(l2_cache_size * 2);140 bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),141 data_sp->GetByteSize(), error);142 ASSERT_TRUE(bytes_read == data_sp->GetByteSize());143 ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2);144 145 // Reading data previously cached (not in L2 cache).146 data_sp->SetByteSize(l2_cache_size + 1);147 bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),148 data_sp->GetByteSize(), error);149 ASSERT_TRUE(bytes_read == data_sp->GetByteSize());150 ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2); // Verify we didn't151 // read from the152 // inferior.153 154 // Read from a different address, but make the size == l2 cache size.155 // This should fill in a the L2 cache.156 data_sp->SetByteSize(l2_cache_size);157 bytes_read = mem_cache.Read(0x2000, data_sp->GetBytes(),158 data_sp->GetByteSize(), error);159 ASSERT_TRUE(bytes_read == data_sp->GetByteSize());160 ASSERT_TRUE(process->m_bytes_left == l2_cache_size);161 162 // Read from that L2 cache entry but read less than size of the cache line.163 // Additionally, read from an offset.164 data_sp->SetByteSize(l2_cache_size - 5);165 bytes_read = mem_cache.Read(0x2001, data_sp->GetBytes(),166 data_sp->GetByteSize(), error);167 ASSERT_TRUE(bytes_read == data_sp->GetByteSize());168 ASSERT_TRUE(process->m_bytes_left == l2_cache_size); // Verify we didn't read169 // from the inferior.170 171 // What happens if we try to populate an L2 cache line but the read gives less172 // than the size of a cache line?173 process->SetMaxReadSize(l2_cache_size - 10);174 data_sp->SetByteSize(l2_cache_size - 5);175 bytes_read = mem_cache.Read(0x3000, data_sp->GetBytes(),176 data_sp->GetByteSize(), error);177 ASSERT_TRUE(bytes_read == l2_cache_size - 10);178 ASSERT_TRUE(process->m_bytes_left == 0);179 180 // What happens if we have a partial L2 cache line filled in and we try to181 // read the part that isn't filled in?182 data_sp->SetByteSize(10);183 bytes_read = mem_cache.Read(0x3000 + l2_cache_size - 10, data_sp->GetBytes(),184 data_sp->GetByteSize(), error);185 ASSERT_TRUE(bytes_read == 0); // The last 10 bytes from this line are186 // missing and we should be reading nothing187 // here.188 189 // What happens when we try to straddle 2 cache lines?190 process->SetMaxReadSize(l2_cache_size * 2);191 data_sp->SetByteSize(l2_cache_size);192 bytes_read = mem_cache.Read(0x4001, data_sp->GetBytes(),193 data_sp->GetByteSize(), error);194 ASSERT_TRUE(bytes_read == l2_cache_size);195 ASSERT_TRUE(process->m_bytes_left == 0);196 197 // What happens when we try to straddle 2 cache lines where the first one is198 // only partially filled?199 process->SetMaxReadSize(l2_cache_size - 1);200 data_sp->SetByteSize(l2_cache_size);201 bytes_read = mem_cache.Read(0x5005, data_sp->GetBytes(),202 data_sp->GetByteSize(), error);203 ASSERT_TRUE(bytes_read == l2_cache_size - 6); // Ignoring the first 5 bytes,204 // missing the last byte205 ASSERT_TRUE(process->m_bytes_left == 0);206 207 // What happens if we add an invalid range and try to do a read larger than208 // a cache line?209 mem_cache.AddInvalidRange(0x6000, l2_cache_size * 2);210 process->SetMaxReadSize(l2_cache_size * 2);211 data_sp->SetByteSize(l2_cache_size * 2);212 bytes_read = mem_cache.Read(0x6000, data_sp->GetBytes(),213 data_sp->GetByteSize(), error);214 ASSERT_TRUE(bytes_read == 0);215 ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2);216 217 // What happens if we add an invalid range and try to do a read lt/eq a218 // cache line?219 mem_cache.AddInvalidRange(0x7000, l2_cache_size);220 process->SetMaxReadSize(l2_cache_size);221 data_sp->SetByteSize(l2_cache_size);222 bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),223 data_sp->GetByteSize(), error);224 ASSERT_TRUE(bytes_read == 0);225 ASSERT_TRUE(process->m_bytes_left == l2_cache_size);226 227 // What happens if we remove the invalid range and read again?228 mem_cache.RemoveInvalidRange(0x7000, l2_cache_size);229 bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),230 data_sp->GetByteSize(), error);231 ASSERT_TRUE(bytes_read == l2_cache_size);232 ASSERT_TRUE(process->m_bytes_left == 0);233 234 // What happens if we flush and read again?235 process->SetMaxReadSize(l2_cache_size * 2);236 mem_cache.Flush(0x7000, l2_cache_size);237 bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),238 data_sp->GetByteSize(), error);239 ASSERT_TRUE(bytes_read == l2_cache_size);240 ASSERT_TRUE(process->m_bytes_left == l2_cache_size); // Verify that we re-read241 // instead of using an242 // old cache243}244 245TEST_F(MemoryTest, TestReadInteger) {246 ArchSpec arch("x86_64-apple-macosx-");247 248 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));249 250 DebuggerSP debugger_sp = Debugger::CreateInstance();251 ASSERT_TRUE(debugger_sp);252 253 TargetSP target_sp = CreateTarget(debugger_sp, arch);254 ASSERT_TRUE(target_sp);255 256 ProcessSP process_sp = CreateProcess(target_sp);257 ASSERT_TRUE(process_sp);258 259 DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());260 Status error;261 262 process->SetFiller(0xff);263 process->SetMaxReadSize(256);264 // The ReadSignedIntegerFromMemory() methods return int64_t. Check that they265 // extend the sign correctly when reading 32-bit values.266 EXPECT_EQ(-1,267 target_sp->ReadSignedIntegerFromMemory(Address(0), 4, 0, error));268 EXPECT_EQ(-1, process->ReadSignedIntegerFromMemory(0, 4, 0, error));269 // Check reading 64-bit values as well.270 EXPECT_EQ(-1,271 target_sp->ReadSignedIntegerFromMemory(Address(0), 8, 0, error));272 EXPECT_EQ(-1, process->ReadSignedIntegerFromMemory(0, 8, 0, error));273 274 // ReadUnsignedIntegerFromMemory() should not extend the sign.275 EXPECT_EQ(0xffffffffULL,276 target_sp->ReadUnsignedIntegerFromMemory(Address(0), 4, 0, error));277 EXPECT_EQ(0xffffffffULL,278 process->ReadUnsignedIntegerFromMemory(0, 4, 0, error));279 EXPECT_EQ(0xffffffffffffffffULL,280 target_sp->ReadUnsignedIntegerFromMemory(Address(0), 8, 0, error));281 EXPECT_EQ(0xffffffffffffffffULL,282 process->ReadUnsignedIntegerFromMemory(0, 8, 0, error));283 284 // Check reading positive values.285 process->GetMemoryCache().Clear();286 process->SetFiller(0x7f);287 process->SetMaxReadSize(256);288 EXPECT_EQ(0x7f7f7f7fLL,289 target_sp->ReadSignedIntegerFromMemory(Address(0), 4, 0, error));290 EXPECT_EQ(0x7f7f7f7fLL, process->ReadSignedIntegerFromMemory(0, 4, 0, error));291 EXPECT_EQ(0x7f7f7f7f7f7f7f7fLL,292 target_sp->ReadSignedIntegerFromMemory(Address(0), 8, 0, error));293 EXPECT_EQ(0x7f7f7f7f7f7f7f7fLL,294 process->ReadSignedIntegerFromMemory(0, 8, 0, error));295}296 297/// A process class that, when asked to read memory from some address X, returns298/// the least significant byte of X.299class DummyReaderProcess : public Process {300public:301 // If true, `DoReadMemory` will not return all requested bytes.302 // It's not possible to control exactly how many bytes will be read, because303 // Process::ReadMemoryFromInferior tries to fulfill the entire request by304 // reading smaller chunks until it gets nothing back.305 bool read_less_than_requested = false;306 bool read_more_than_requested = false;307 308 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,309 Status &error) override {310 if (read_less_than_requested && size > 0)311 size--;312 if (read_more_than_requested)313 size *= 2;314 uint8_t *buffer = static_cast<uint8_t *>(buf);315 for (lldb::addr_t addr = vm_addr; addr < vm_addr + size; addr++)316 buffer[addr - vm_addr] = static_cast<uint8_t>(addr); // LSB of addr.317 return size;318 }319 // Boilerplate, nothing interesting below.320 DummyReaderProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)321 : Process(target_sp, listener_sp) {}322 bool CanDebug(lldb::TargetSP, bool) override { return true; }323 Status DoDestroy() override { return {}; }324 void RefreshStateAfterStop() override {}325 bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }326 llvm::StringRef GetPluginName() override { return "Dummy"; }327};328 329TEST_F(MemoryTest, TestReadMemoryRanges) {330 ArchSpec arch("x86_64-apple-macosx-");331 332 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));333 334 DebuggerSP debugger_sp = Debugger::CreateInstance();335 ASSERT_TRUE(debugger_sp);336 337 TargetSP target_sp = CreateTarget(debugger_sp, arch);338 ASSERT_TRUE(target_sp);339 340 ListenerSP listener_sp(Listener::MakeListener("dummy"));341 ProcessSP process_sp =342 std::make_shared<DummyReaderProcess>(target_sp, listener_sp);343 ASSERT_TRUE(process_sp);344 345 {346 llvm::SmallVector<uint8_t, 0> buffer(1024, 0);347 // Read 8 ranges of 128 bytes with arbitrary base addresses.348 llvm::SmallVector<Range<addr_t, size_t>> ranges = {349 {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128},350 {0xffaabbccdd, 128}, {0x0, 128}, {0x4242424242, 128},351 {0x17171717, 128}, {0x99999, 128}};352 353 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results =354 process_sp->ReadMemoryRanges(ranges, buffer);355 356 for (auto [range, memory] : llvm::zip(ranges, read_results)) {357 ASSERT_EQ(memory.size(), 128u);358 addr_t range_base = range.GetRangeBase();359 for (auto [idx, byte] : llvm::enumerate(memory))360 ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx));361 }362 }363 364 auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp);365 dummy_process.read_less_than_requested = true;366 {367 llvm::SmallVector<uint8_t, 0> buffer(1024, 0);368 llvm::SmallVector<Range<addr_t, size_t>> ranges = {369 {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128}};370 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results =371 dummy_process.ReadMemoryRanges(ranges, buffer);372 for (auto [range, memory] : llvm::zip(ranges, read_results)) {373 ASSERT_LT(memory.size(), 128u);374 addr_t range_base = range.GetRangeBase();375 for (auto [idx, byte] : llvm::enumerate(memory))376 ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx));377 }378 }379}380 381using MemoryDeathTest = MemoryTest;382 383TEST_F(MemoryDeathTest, TestReadMemoryRangesReturnsTooMuch) {384 ArchSpec arch("x86_64-apple-macosx-");385 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));386 DebuggerSP debugger_sp = Debugger::CreateInstance();387 ASSERT_TRUE(debugger_sp);388 TargetSP target_sp = CreateTarget(debugger_sp, arch);389 ASSERT_TRUE(target_sp);390 ListenerSP listener_sp(Listener::MakeListener("dummy"));391 ProcessSP process_sp =392 std::make_shared<DummyReaderProcess>(target_sp, listener_sp);393 ASSERT_TRUE(process_sp);394 395 auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp);396 dummy_process.read_more_than_requested = true;397 llvm::SmallVector<uint8_t, 0> buffer(1024, 0);398 llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128}};399 400 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;401 ASSERT_DEBUG_DEATH(402 { read_results = process_sp->ReadMemoryRanges(ranges, buffer); },403 "read more than requested bytes");404#ifdef NDEBUG405 // With asserts off, the read should return empty ranges.406 ASSERT_EQ(read_results.size(), 1u);407 ASSERT_TRUE(read_results[0].empty());408#endif409}410 411TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) {412 ArchSpec arch("x86_64-apple-macosx-");413 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));414 DebuggerSP debugger_sp = Debugger::CreateInstance();415 ASSERT_TRUE(debugger_sp);416 TargetSP target_sp = CreateTarget(debugger_sp, arch);417 ASSERT_TRUE(target_sp);418 ListenerSP listener_sp(Listener::MakeListener("dummy"));419 ProcessSP process_sp =420 std::make_shared<DummyReaderProcess>(target_sp, listener_sp);421 ASSERT_TRUE(process_sp);422 423 llvm::SmallVector<uint8_t, 0> short_buffer(10, 0);424 llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128},425 {0x11, 128}};426 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;427 ASSERT_DEBUG_DEATH(428 { read_results = process_sp->ReadMemoryRanges(ranges, short_buffer); },429 "provided buffer is too short");430#ifdef NDEBUG431 // With asserts off, the read should return empty ranges.432 ASSERT_EQ(read_results.size(), ranges.size());433 for (llvm::MutableArrayRef<uint8_t> result : read_results)434 ASSERT_TRUE(result.empty());435#endif436}437