|
17 | 17 | #include "lldb/Utility/ArchSpec.h" |
18 | 18 | #include "lldb/Utility/DataBufferHeap.h" |
19 | 19 | #include "gtest/gtest.h" |
| 20 | +#include <cstdint> |
20 | 21 |
|
21 | 22 | using namespace lldb_private; |
22 | 23 | using namespace lldb; |
@@ -225,3 +226,144 @@ TEST_F(MemoryTest, TesetMemoryCacheRead) { |
225 | 226 | // instead of using an |
226 | 227 | // old cache |
227 | 228 | } |
| 229 | + |
| 230 | +/// A process class that, when asked to read memory from some address X, returns |
| 231 | +/// the least significant byte of X. |
| 232 | +class DummyReaderProcess : public Process { |
| 233 | +public: |
| 234 | + // If true, `DoReadMemory` will not return all requested bytes. |
| 235 | + // It's not possible to control exactly how many bytes will be read, because |
| 236 | + // Process::ReadMemoryFromInferior tries to fulfill the entire request by |
| 237 | + // reading smaller chunks until it gets nothing back. |
| 238 | + bool read_less_than_requested = false; |
| 239 | + bool read_more_than_requested = false; |
| 240 | + |
| 241 | + size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, |
| 242 | + Status &error) override { |
| 243 | + if (read_less_than_requested && size > 0) |
| 244 | + size--; |
| 245 | + if (read_more_than_requested) |
| 246 | + size *= 2; |
| 247 | + uint8_t *buffer = static_cast<uint8_t *>(buf); |
| 248 | + for (size_t addr = vm_addr; addr < vm_addr + size; addr++) |
| 249 | + buffer[addr - vm_addr] = static_cast<uint8_t>(addr); // LSB of addr. |
| 250 | + return size; |
| 251 | + } |
| 252 | + // Boilerplate, nothing interesting below. |
| 253 | + DummyReaderProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) |
| 254 | + : Process(target_sp, listener_sp) {} |
| 255 | + bool CanDebug(lldb::TargetSP, bool) override { return true; } |
| 256 | + Status DoDestroy() override { return {}; } |
| 257 | + void RefreshStateAfterStop() override {} |
| 258 | + bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; } |
| 259 | + llvm::StringRef GetPluginName() override { return "Dummy"; } |
| 260 | +}; |
| 261 | + |
| 262 | +TEST_F(MemoryTest, TestReadMemoryRanges) { |
| 263 | + ArchSpec arch("x86_64-apple-macosx-"); |
| 264 | + |
| 265 | + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); |
| 266 | + |
| 267 | + DebuggerSP debugger_sp = Debugger::CreateInstance(); |
| 268 | + ASSERT_TRUE(debugger_sp); |
| 269 | + |
| 270 | + TargetSP target_sp = CreateTarget(debugger_sp, arch); |
| 271 | + ASSERT_TRUE(target_sp); |
| 272 | + |
| 273 | + ListenerSP listener_sp(Listener::MakeListener("dummy")); |
| 274 | + ProcessSP process_sp = |
| 275 | + std::make_shared<DummyReaderProcess>(target_sp, listener_sp); |
| 276 | + ASSERT_TRUE(process_sp); |
| 277 | + |
| 278 | + { |
| 279 | + llvm::SmallVector<uint8_t, 0> buffer(1024, 0); |
| 280 | + // Read 8 ranges of 128 bytes with arbitrary base addresses. |
| 281 | + llvm::SmallVector<Range<addr_t, size_t>> ranges = { |
| 282 | + {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128}, |
| 283 | + {0xffaabbccdd, 128}, {0x0, 128}, {0x4242424242, 128}, |
| 284 | + {0x17171717, 128}, {0x99999, 128}}; |
| 285 | + |
| 286 | + llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results = |
| 287 | + process_sp->ReadMemoryRanges(ranges, buffer); |
| 288 | + |
| 289 | + for (auto [range, memory] : llvm::zip(ranges, read_results)) { |
| 290 | + ASSERT_EQ(memory.size(), 128u); |
| 291 | + addr_t range_base = range.GetRangeBase(); |
| 292 | + for (auto [idx, byte] : llvm::enumerate(memory)) |
| 293 | + ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx)); |
| 294 | + } |
| 295 | + } |
| 296 | + |
| 297 | + auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp); |
| 298 | + dummy_process.read_less_than_requested = true; |
| 299 | + { |
| 300 | + llvm::SmallVector<uint8_t, 0> buffer(1024, 0); |
| 301 | + llvm::SmallVector<Range<addr_t, size_t>> ranges = { |
| 302 | + {0x12345, 128}, {0x11112222, 128}, {0x77777777, 128}}; |
| 303 | + llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results = |
| 304 | + dummy_process.ReadMemoryRanges(ranges, buffer); |
| 305 | + for (auto [range, memory] : llvm::zip(ranges, read_results)) { |
| 306 | + ASSERT_LT(memory.size(), 128u); |
| 307 | + addr_t range_base = range.GetRangeBase(); |
| 308 | + for (auto [idx, byte] : llvm::enumerate(memory)) |
| 309 | + ASSERT_EQ(byte, static_cast<uint8_t>(range_base + idx)); |
| 310 | + } |
| 311 | + } |
| 312 | +} |
| 313 | + |
| 314 | +using MemoryDeathTest = MemoryTest; |
| 315 | + |
| 316 | +TEST_F(MemoryDeathTest, TestReadMemoryRangesReturnsTooMuch) { |
| 317 | + ArchSpec arch("x86_64-apple-macosx-"); |
| 318 | + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); |
| 319 | + DebuggerSP debugger_sp = Debugger::CreateInstance(); |
| 320 | + ASSERT_TRUE(debugger_sp); |
| 321 | + TargetSP target_sp = CreateTarget(debugger_sp, arch); |
| 322 | + ASSERT_TRUE(target_sp); |
| 323 | + ListenerSP listener_sp(Listener::MakeListener("dummy")); |
| 324 | + ProcessSP process_sp = |
| 325 | + std::make_shared<DummyReaderProcess>(target_sp, listener_sp); |
| 326 | + ASSERT_TRUE(process_sp); |
| 327 | + |
| 328 | + auto &dummy_process = static_cast<DummyReaderProcess &>(*process_sp); |
| 329 | + dummy_process.read_more_than_requested = true; |
| 330 | + llvm::SmallVector<uint8_t, 0> buffer(1024, 0); |
| 331 | + llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128}}; |
| 332 | + |
| 333 | + llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results; |
| 334 | + ASSERT_DEBUG_DEATH( |
| 335 | + { read_results = process_sp->ReadMemoryRanges(ranges, buffer); }, |
| 336 | + "read more than requested bytes"); |
| 337 | +#ifdef NDEBUG |
| 338 | + // With asserts off, the read should return empty ranges. |
| 339 | + ASSERT_EQ(read_results.size(), 1u); |
| 340 | + ASSERT_TRUE(read_results[0].empty()); |
| 341 | +#endif |
| 342 | +} |
| 343 | + |
| 344 | +TEST_F(MemoryDeathTest, TestReadMemoryRangesWithShortBuffer) { |
| 345 | + ArchSpec arch("x86_64-apple-macosx-"); |
| 346 | + Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); |
| 347 | + DebuggerSP debugger_sp = Debugger::CreateInstance(); |
| 348 | + ASSERT_TRUE(debugger_sp); |
| 349 | + TargetSP target_sp = CreateTarget(debugger_sp, arch); |
| 350 | + ASSERT_TRUE(target_sp); |
| 351 | + ListenerSP listener_sp(Listener::MakeListener("dummy")); |
| 352 | + ProcessSP process_sp = |
| 353 | + std::make_shared<DummyReaderProcess>(target_sp, listener_sp); |
| 354 | + ASSERT_TRUE(process_sp); |
| 355 | + |
| 356 | + llvm::SmallVector<uint8_t, 0> short_buffer(10, 0); |
| 357 | + llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0x12345, 128}, |
| 358 | + {0x11, 128}}; |
| 359 | + llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results; |
| 360 | + ASSERT_DEBUG_DEATH( |
| 361 | + { read_results = process_sp->ReadMemoryRanges(ranges, short_buffer); }, |
| 362 | + "provided buffer is too short"); |
| 363 | +#ifdef NDEBUG |
| 364 | + // With asserts off, the read should return empty ranges. |
| 365 | + ASSERT_EQ(read_results.size(), ranges.size()); |
| 366 | + for (llvm::MutableArrayRef<uint8_t> result : read_results) |
| 367 | + ASSERT_TRUE(result.empty()); |
| 368 | +#endif |
| 369 | +} |
0 commit comments