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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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