1 //===-- MemoryTest.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace lldb_private
;
22 using namespace lldb_private::repro
;
26 class MemoryTest
: public ::testing::Test
{
28 void SetUp() override
{
29 FileSystem::Initialize();
30 HostInfo::Initialize();
31 PlatformMacOSX::Initialize();
33 void TearDown() override
{
34 PlatformMacOSX::Terminate();
35 HostInfo::Terminate();
36 FileSystem::Terminate();
40 class DummyProcess
: public Process
{
42 DummyProcess(lldb::TargetSP target_sp
, lldb::ListenerSP listener_sp
)
43 : Process(target_sp
, listener_sp
), m_bytes_left(0) {}
46 bool CanDebug(lldb::TargetSP target
, bool plugin_specified_by_name
) override
{
49 Status
DoDestroy() override
{ return {}; }
50 void RefreshStateAfterStop() override
{}
51 size_t DoReadMemory(lldb::addr_t vm_addr
, void *buf
, size_t size
,
52 Status
&error
) override
{
53 if (m_bytes_left
== 0)
56 size_t num_bytes_to_write
= size
;
57 if (m_bytes_left
< size
) {
58 num_bytes_to_write
= m_bytes_left
;
64 memset(buf
, 'B', num_bytes_to_write
);
65 return num_bytes_to_write
;
67 bool DoUpdateThreadList(ThreadList
&old_thread_list
,
68 ThreadList
&new_thread_list
) override
{
71 llvm::StringRef
GetPluginName() override
{ return "Dummy"; }
73 // Test-specific additions
75 MemoryCache
&GetMemoryCache() { return m_memory_cache
; }
76 void SetMaxReadSize(size_t size
) { m_bytes_left
= size
; }
80 TargetSP
CreateTarget(DebuggerSP
&debugger_sp
, ArchSpec
&arch
) {
81 PlatformSP platform_sp
;
83 debugger_sp
->GetTargetList().CreateTarget(
84 *debugger_sp
, "", arch
, eLoadDependentsNo
, platform_sp
, target_sp
);
88 TEST_F(MemoryTest
, TesetMemoryCacheRead
) {
89 ArchSpec
arch("x86_64-apple-macosx-");
91 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch
));
93 DebuggerSP debugger_sp
= Debugger::CreateInstance();
94 ASSERT_TRUE(debugger_sp
);
96 TargetSP target_sp
= CreateTarget(debugger_sp
, arch
);
97 ASSERT_TRUE(target_sp
);
99 ListenerSP
listener_sp(Listener::MakeListener("dummy"));
100 ProcessSP process_sp
= std::make_shared
<DummyProcess
>(target_sp
, listener_sp
);
101 ASSERT_TRUE(process_sp
);
103 DummyProcess
*process
= static_cast<DummyProcess
*>(process_sp
.get());
104 MemoryCache
&mem_cache
= process
->GetMemoryCache();
105 const uint64_t l2_cache_size
= process
->GetMemoryCacheLineSize();
107 auto data_sp
= std::make_shared
<DataBufferHeap
>(l2_cache_size
* 2, '\0');
108 size_t bytes_read
= 0;
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);
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);
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);
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
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
);
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.
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);
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
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);
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);
192 // What happens if we add an invalid range and try to do a read larger than
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);
202 // What happens if we add an invalid range and try to do a read lt/eq a
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
);
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);
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