xref: /llvm-project/lldb/unittests/Target/MemoryTest.cpp (revision f341d7a4091a6ec1c1a4a49c4810d4a8b036ad60)
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-exception
6 //
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 
21 using namespace lldb_private;
22 using namespace lldb_private::repro;
23 using namespace lldb;
24 
25 namespace {
26 class MemoryTest : public ::testing::Test {
27 public:
SetUp()28   void SetUp() override {
29     FileSystem::Initialize();
30     HostInfo::Initialize();
31     PlatformMacOSX::Initialize();
32   }
TearDown()33   void TearDown() override {
34     PlatformMacOSX::Terminate();
35     HostInfo::Terminate();
36     FileSystem::Terminate();
37   }
38 };
39 
40 class DummyProcess : public Process {
41 public:
DummyProcess(lldb::TargetSP target_sp,lldb::ListenerSP listener_sp)42   DummyProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
43       : Process(target_sp, listener_sp), m_bytes_left(0) {}
44 
45   // Required overrides
CanDebug(lldb::TargetSP target,bool plugin_specified_by_name)46   bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
47     return true;
48   }
DoDestroy()49   Status DoDestroy() override { return {}; }
RefreshStateAfterStop()50   void RefreshStateAfterStop() override {}
DoReadMemory(lldb::addr_t vm_addr,void * buf,size_t size,Status & error)51   size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
52                       Status &error) override {
53     if (m_bytes_left == 0)
54       return 0;
55 
56     size_t num_bytes_to_write = size;
57     if (m_bytes_left < size) {
58       num_bytes_to_write = m_bytes_left;
59       m_bytes_left = 0;
60     } else {
61       m_bytes_left -= size;
62     }
63 
64     memset(buf, 'B', num_bytes_to_write);
65     return num_bytes_to_write;
66   }
DoUpdateThreadList(ThreadList & old_thread_list,ThreadList & new_thread_list)67   bool DoUpdateThreadList(ThreadList &old_thread_list,
68                           ThreadList &new_thread_list) override {
69     return false;
70   }
GetPluginName()71   llvm::StringRef GetPluginName() override { return "Dummy"; }
72 
73   // Test-specific additions
74   size_t m_bytes_left;
GetMemoryCache()75   MemoryCache &GetMemoryCache() { return m_memory_cache; }
SetMaxReadSize(size_t size)76   void SetMaxReadSize(size_t size) { m_bytes_left = size; }
77 };
78 } // namespace
79 
CreateTarget(DebuggerSP & debugger_sp,ArchSpec & arch)80 TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) {
81   PlatformSP platform_sp;
82   TargetSP target_sp;
83   debugger_sp->GetTargetList().CreateTarget(
84       *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
85   return target_sp;
86 }
87 
TEST_F(MemoryTest,TesetMemoryCacheRead)88 TEST_F(MemoryTest, TesetMemoryCacheRead) {
89   ArchSpec arch("x86_64-apple-macosx-");
90 
91   Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
92 
93   DebuggerSP debugger_sp = Debugger::CreateInstance();
94   ASSERT_TRUE(debugger_sp);
95 
96   TargetSP target_sp = CreateTarget(debugger_sp, arch);
97   ASSERT_TRUE(target_sp);
98 
99   ListenerSP listener_sp(Listener::MakeListener("dummy"));
100   ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);
101   ASSERT_TRUE(process_sp);
102 
103   DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
104   MemoryCache &mem_cache = process->GetMemoryCache();
105   const uint64_t l2_cache_size = process->GetMemoryCacheLineSize();
106   Status error;
107   auto data_sp = std::make_shared<DataBufferHeap>(l2_cache_size * 2, '\0');
108   size_t bytes_read = 0;
109 
110   // Cache empty, memory read fails, size > l2 cache size
111   process->SetMaxReadSize(0);
112   bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),
113                               data_sp->GetByteSize(), error);
114   ASSERT_TRUE(bytes_read == 0);
115 
116   // Cache empty, memory read fails, size <= l2 cache size
117   data_sp->SetByteSize(l2_cache_size);
118   bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),
119                               data_sp->GetByteSize(), error);
120   ASSERT_TRUE(bytes_read == 0);
121 
122   // Cache empty, memory read succeeds, size > l2 cache size
123   process->SetMaxReadSize(l2_cache_size * 4);
124   data_sp->SetByteSize(l2_cache_size * 2);
125   bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),
126                               data_sp->GetByteSize(), error);
127   ASSERT_TRUE(bytes_read == data_sp->GetByteSize());
128   ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2);
129 
130   // Reading data previously cached (not in L2 cache).
131   data_sp->SetByteSize(l2_cache_size + 1);
132   bytes_read = mem_cache.Read(0x1000, data_sp->GetBytes(),
133                               data_sp->GetByteSize(), error);
134   ASSERT_TRUE(bytes_read == data_sp->GetByteSize());
135   ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2); // Verify we didn't
136                                                            // read from the
137                                                            // inferior.
138 
139   // Read from a different address, but make the size == l2 cache size.
140   // This should fill in a the L2 cache.
141   data_sp->SetByteSize(l2_cache_size);
142   bytes_read = mem_cache.Read(0x2000, data_sp->GetBytes(),
143                               data_sp->GetByteSize(), error);
144   ASSERT_TRUE(bytes_read == data_sp->GetByteSize());
145   ASSERT_TRUE(process->m_bytes_left == l2_cache_size);
146 
147   // Read from that L2 cache entry but read less than size of the cache line.
148   // Additionally, read from an offset.
149   data_sp->SetByteSize(l2_cache_size - 5);
150   bytes_read = mem_cache.Read(0x2001, data_sp->GetBytes(),
151                               data_sp->GetByteSize(), error);
152   ASSERT_TRUE(bytes_read == data_sp->GetByteSize());
153   ASSERT_TRUE(process->m_bytes_left == l2_cache_size); // Verify we didn't read
154                                                        // from the inferior.
155 
156   // What happens if we try to populate an L2 cache line but the read gives less
157   // than the size of a cache line?
158   process->SetMaxReadSize(l2_cache_size - 10);
159   data_sp->SetByteSize(l2_cache_size - 5);
160   bytes_read = mem_cache.Read(0x3000, data_sp->GetBytes(),
161                               data_sp->GetByteSize(), error);
162   ASSERT_TRUE(bytes_read == l2_cache_size - 10);
163   ASSERT_TRUE(process->m_bytes_left == 0);
164 
165   // What happens if we have a partial L2 cache line filled in and we try to
166   // read the part that isn't filled in?
167   data_sp->SetByteSize(10);
168   bytes_read = mem_cache.Read(0x3000 + l2_cache_size - 10, data_sp->GetBytes(),
169                               data_sp->GetByteSize(), error);
170   ASSERT_TRUE(bytes_read == 0); // The last 10 bytes from this line are
171                                 // missing and we should be reading nothing
172                                 // here.
173 
174   // What happens when we try to straddle 2 cache lines?
175   process->SetMaxReadSize(l2_cache_size * 2);
176   data_sp->SetByteSize(l2_cache_size);
177   bytes_read = mem_cache.Read(0x4001, data_sp->GetBytes(),
178                               data_sp->GetByteSize(), error);
179   ASSERT_TRUE(bytes_read == l2_cache_size);
180   ASSERT_TRUE(process->m_bytes_left == 0);
181 
182   // What happens when we try to straddle 2 cache lines where the first one is
183   // only partially filled?
184   process->SetMaxReadSize(l2_cache_size - 1);
185   data_sp->SetByteSize(l2_cache_size);
186   bytes_read = mem_cache.Read(0x5005, data_sp->GetBytes(),
187                               data_sp->GetByteSize(), error);
188   ASSERT_TRUE(bytes_read == l2_cache_size - 6); // Ignoring the first 5 bytes,
189                                                 // missing the last byte
190   ASSERT_TRUE(process->m_bytes_left == 0);
191 
192   // What happens if we add an invalid range and try to do a read larger than
193   // a cache line?
194   mem_cache.AddInvalidRange(0x6000, l2_cache_size * 2);
195   process->SetMaxReadSize(l2_cache_size * 2);
196   data_sp->SetByteSize(l2_cache_size * 2);
197   bytes_read = mem_cache.Read(0x6000, data_sp->GetBytes(),
198                               data_sp->GetByteSize(), error);
199   ASSERT_TRUE(bytes_read == 0);
200   ASSERT_TRUE(process->m_bytes_left == l2_cache_size * 2);
201 
202   // What happens if we add an invalid range and try to do a read lt/eq a
203   // cache line?
204   mem_cache.AddInvalidRange(0x7000, l2_cache_size);
205   process->SetMaxReadSize(l2_cache_size);
206   data_sp->SetByteSize(l2_cache_size);
207   bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),
208                               data_sp->GetByteSize(), error);
209   ASSERT_TRUE(bytes_read == 0);
210   ASSERT_TRUE(process->m_bytes_left == l2_cache_size);
211 
212   // What happens if we remove the invalid range and read again?
213   mem_cache.RemoveInvalidRange(0x7000, l2_cache_size);
214   bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),
215                               data_sp->GetByteSize(), error);
216   ASSERT_TRUE(bytes_read == l2_cache_size);
217   ASSERT_TRUE(process->m_bytes_left == 0);
218 
219   // What happens if we flush and read again?
220   process->SetMaxReadSize(l2_cache_size * 2);
221   mem_cache.Flush(0x7000, l2_cache_size);
222   bytes_read = mem_cache.Read(0x7000, data_sp->GetBytes(),
223                               data_sp->GetByteSize(), error);
224   ASSERT_TRUE(bytes_read == l2_cache_size);
225   ASSERT_TRUE(process->m_bytes_left == l2_cache_size); // Verify that we re-read
226                                                        // instead of using an
227                                                        // old cache
228 }
229