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
21using namespace lldb_private;
22using namespace lldb_private::repro;
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 overrides
46 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 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(s: buf, c: 'B', n: num_bytes_to_write);
65 return num_bytes_to_write;
66 }
67 bool DoUpdateThreadList(ThreadList &old_thread_list,
68 ThreadList &new_thread_list) override {
69 return false;
70 }
71 llvm::StringRef GetPluginName() override { return "Dummy"; }
72
73 // Test-specific additions
74 size_t m_bytes_left;
75 MemoryCache &GetMemoryCache() { return m_memory_cache; }
76 void SetMaxReadSize(size_t size) { m_bytes_left = size; }
77};
78} // namespace
79
80TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) {
81 PlatformSP platform_sp;
82 TargetSP target_sp;
83 debugger_sp->GetTargetList().CreateTarget(
84 debugger&: *debugger_sp, user_exe_path: "", arch, get_dependent_modules: eLoadDependentsNo, platform_sp, target_sp);
85 return target_sp;
86}
87
88TEST_F(MemoryTest, TesetMemoryCacheRead) {
89 ArchSpec arch("x86_64-apple-macosx-");
90
91 Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(force: true, arch: &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(name: "dummy"));
100 ProcessSP process_sp = std::make_shared<DummyProcess>(args&: target_sp, args&: 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>(args: l2_cache_size * 2, args: '\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(addr: 0x1000, dst: data_sp->GetBytes(),
113 dst_len: 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(addr: 0x1000, dst: data_sp->GetBytes(),
119 dst_len: 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(addr: 0x1000, dst: data_sp->GetBytes(),
126 dst_len: 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(addr: 0x1000, dst: data_sp->GetBytes(),
133 dst_len: 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(addr: 0x2000, dst: data_sp->GetBytes(),
143 dst_len: 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(addr: 0x2001, dst: data_sp->GetBytes(),
151 dst_len: 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(addr: 0x3000, dst: data_sp->GetBytes(),
161 dst_len: 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(addr: 0x3000 + l2_cache_size - 10, dst: data_sp->GetBytes(),
169 dst_len: 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(addr: 0x4001, dst: data_sp->GetBytes(),
178 dst_len: 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(addr: 0x5005, dst: data_sp->GetBytes(),
187 dst_len: 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(base_addr: 0x6000, byte_size: 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(addr: 0x6000, dst: data_sp->GetBytes(),
198 dst_len: 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(base_addr: 0x7000, byte_size: l2_cache_size);
205 process->SetMaxReadSize(l2_cache_size);
206 data_sp->SetByteSize(l2_cache_size);
207 bytes_read = mem_cache.Read(addr: 0x7000, dst: data_sp->GetBytes(),
208 dst_len: 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(base_addr: 0x7000, byte_size: l2_cache_size);
214 bytes_read = mem_cache.Read(addr: 0x7000, dst: data_sp->GetBytes(),
215 dst_len: 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(addr: 0x7000, size: l2_cache_size);
222 bytes_read = mem_cache.Read(addr: 0x7000, dst: data_sp->GetBytes(),
223 dst_len: 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

source code of lldb/unittests/Target/MemoryTest.cpp