77// ===----------------------------------------------------------------------===//
88#include " lldb/Expression/DWARFExpression.h"
99#include " ValueMatcher.h"
10+ #include < unordered_map>
1011#ifdef ARCH_AARCH64
1112#include " Plugins/ABI/AArch64/ABISysV_arm64.h"
1213#endif
@@ -39,49 +40,75 @@ using namespace lldb_private;
3940using namespace llvm ::dwarf;
4041
4142namespace {
42- struct MockProcess : Process {
43- MockProcess (lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
44- : Process(target_sp, listener_sp) {}
45-
46- llvm::StringRef GetPluginName () override { return " mock process" ; }
43+ // / Mock memory implementation for testing.
44+ // / Stores predefined memory contents indexed by {address, size} pairs.
45+ class MockMemory {
46+ public:
47+ // / Represents a memory read request with an address and size.
48+ // / Used as a key in the memory map to look up predefined test data.
49+ struct Request {
50+ lldb::addr_t addr;
51+ size_t size;
52+
53+ bool operator ==(const Request &other) const {
54+ return addr == other.addr && size == other.size ;
55+ }
4756
48- bool CanDebug (lldb::TargetSP target, bool plugin_specified_by_name) override {
49- return false ;
57+ // / Hash function for Request to enable its use in unordered_map.
58+ struct Hash {
59+ size_t operator ()(const Request &req) const {
60+ size_t h1 = std::hash<lldb::addr_t >{}(req.addr );
61+ size_t h2 = std::hash<size_t >{}(req.size );
62+ return h1 ^ (h2 << 1 );
63+ }
64+ };
5065 };
5166
52- Status DoDestroy () override { return {}; }
53-
54- void RefreshStateAfterStop () override {}
55-
56- bool DoUpdateThreadList (ThreadList &old_thread_list,
57- ThreadList &new_thread_list) override {
58- return false ;
59- };
67+ typedef std::unordered_map<Request, std::vector<uint8_t >, Request::Hash> Map;
68+ MockMemory () = default ;
69+ MockMemory (Map memory) : m_memory(std::move(memory)) {
70+ // Make sure the requested memory size matches the returned value.
71+ for (auto &kv : m_memory) {
72+ auto &req = kv.first ;
73+ auto &bytes = kv.second ;
74+ assert (bytes.size () == req.size );
75+ }
76+ }
6077
61- size_t DoReadMemory (lldb::addr_t vm_addr, void *buf, size_t size,
62- Status &error) override {
63- for (size_t i = 0 ; i < size; ++i)
64- ((char *)buf)[i] = (vm_addr + i) & 0xff ;
65- error.Clear ();
66- return size;
78+ llvm::Expected<std::vector<uint8_t >> ReadMemory (lldb::addr_t addr,
79+ size_t size) {
80+ if (!m_memory.count ({addr, size})) {
81+ return llvm::createStringError (
82+ llvm::inconvertibleErrorCode (),
83+ " MockMemory::ReadMemory {address=0x%" PRIx64 " , size=%zu} not found" ,
84+ addr, size);
85+ }
86+ return m_memory[{addr, size}];
6787 }
88+
89+ private:
90+ std::unordered_map<Request, std::vector<uint8_t >, Request::Hash> m_memory;
6891};
6992
70- // / A Process whose `ReadMemory` override queries a DenseMap .
71- struct MockProcessWithMemRead : Process {
93+ // / A Process whose `ReadMemory` override queries MockMemory .
94+ struct MockProcess : Process {
7295 using addr_t = lldb::addr_t ;
7396
74- llvm::DenseMap< addr_t , addr_t > memory_map ;
97+ MockMemory m_memory ;
7598
76- MockProcessWithMemRead (lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
77- llvm::DenseMap< addr_t , addr_t > &&memory_map )
78- : Process(target_sp, listener_sp), memory_map(memory_map ) {}
99+ MockProcess (lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
100+ MockMemory memory )
101+ : Process(target_sp, listener_sp), m_memory(std::move(memory) ) {}
79102 size_t DoReadMemory (addr_t vm_addr, void *buf, size_t size,
80103 Status &error) override {
81- assert (memory_map.contains (vm_addr));
82- assert (size == sizeof (addr_t ));
83- *reinterpret_cast <addr_t *>(buf) = memory_map[vm_addr];
84- return sizeof (addr_t );
104+ auto expected_memory = m_memory.ReadMemory (vm_addr, size);
105+ if (!expected_memory) {
106+ error = Status::FromError (expected_memory.takeError ());
107+ return 0 ;
108+ }
109+ assert (expected_memory->size () == size);
110+ std::memcpy (buf, expected_memory->data (), expected_memory->size ());
111+ return size;
85112 }
86113 size_t ReadMemory (addr_t addr, void *buf, size_t size,
87114 Status &status) override {
@@ -202,6 +229,35 @@ class DWARFExpressionMockProcessTest : public ::testing::Test {
202229 }
203230};
204231
232+ // / Mock target implementation for testing.
233+ // / Provides predefined memory contents via MockMemory instead of reading from
234+ // / a real process.
235+ class MockTarget : public Target {
236+ public:
237+ MockTarget (Debugger &debugger, const ArchSpec &target_arch,
238+ const lldb::PlatformSP &platform_sp, MockMemory memory)
239+ : Target(debugger, target_arch, platform_sp, true ),
240+ m_memory (std::move(memory)) {}
241+
242+ size_t ReadMemory (const Address &addr, void *dst, size_t dst_len,
243+ Status &error, bool force_live_memory = false ,
244+ lldb::addr_t *load_addr_ptr = nullptr ,
245+ bool *did_read_live_memory = nullptr ) override {
246+ auto expected_memory = m_memory.ReadMemory (addr.GetOffset (), dst_len);
247+ if (!expected_memory) {
248+ error = Status::FromError (expected_memory.takeError ());
249+ return 0 ;
250+ }
251+ const size_t bytes_read = expected_memory->size ();
252+ assert (bytes_read <= dst_len);
253+ std::memcpy (dst, expected_memory->data (), bytes_read);
254+ return bytes_read;
255+ }
256+
257+ private:
258+ MockMemory m_memory;
259+ };
260+
205261struct TestContext {
206262 lldb::PlatformSP platform_sp;
207263 lldb::TargetSP target_sp;
@@ -213,30 +269,34 @@ struct TestContext {
213269
214270// / A helper function to create TestContext objects with the
215271// / given triple, memory, and register contents.
216- static bool CreateTestContext (
217- TestContext *ctx, llvm::StringRef triple ,
218- std::optional<RegisterValue> reg_value = {},
219- std::optional<llvm::DenseMap<lldb:: addr_t , lldb:: addr_t >> memory = {}) {
272+ static bool CreateTestContext (TestContext *ctx, llvm::StringRef triple,
273+ std::optional<RegisterValue> reg_value = {} ,
274+ std::optional<MockMemory> process_memory = {},
275+ std::optional<MockMemory> target_memory = {}) {
220276 ArchSpec arch (triple);
221- Platform::SetHostPlatform (
222- platform_linux::PlatformLinux::CreateInstance (true , &arch)) ;
223- lldb::PlatformSP platform_sp;
277+ lldb::PlatformSP platform_sp =
278+ platform_linux::PlatformLinux::CreateInstance (true , &arch);
279+ Platform::SetHostPlatform ( platform_sp) ;
224280 lldb::TargetSP target_sp;
225281 lldb::DebuggerSP debugger_sp = Debugger::CreateInstance ();
226- Status status = debugger_sp->GetTargetList ().CreateTarget (
227- *debugger_sp, " " , arch, eLoadDependentsNo, platform_sp, target_sp);
282+
283+ Status status;
284+ if (target_memory)
285+ target_sp = std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp,
286+ std::move (*target_memory));
287+ else
288+ status = debugger_sp->GetTargetList ().CreateTarget (
289+ *debugger_sp, " " , arch, eLoadDependentsNo, platform_sp, target_sp);
228290
229291 EXPECT_TRUE (status.Success ());
230292 if (!status.Success ())
231293 return false ;
232294
233295 lldb::ProcessSP process_sp;
234- if (memory)
235- process_sp = std::make_shared<MockProcessWithMemRead>(
236- target_sp, Listener::MakeListener (" dummy" ), std::move (*memory));
237- else
238- process_sp = std::make_shared<MockProcess>(target_sp,
239- Listener::MakeListener (" dummy" ));
296+ if (!process_memory)
297+ process_memory = MockMemory ();
298+ process_sp = std::make_shared<MockProcess>(
299+ target_sp, Listener::MakeListener (" dummy" ), std::move (*process_memory));
240300
241301 auto thread_sp = std::make_shared<MockThread>(*process_sp);
242302
@@ -253,35 +313,6 @@ static bool CreateTestContext(
253313 return true ;
254314}
255315
256- // NB: This class doesn't use the override keyword to avoid
257- // -Winconsistent-missing-override warnings from the compiler. The
258- // inconsistency comes from the overriding definitions in the MOCK_*** macros.
259- class MockTarget : public Target {
260- public:
261- MockTarget (Debugger &debugger, const ArchSpec &target_arch,
262- const lldb::PlatformSP &platform_sp)
263- : Target(debugger, target_arch, platform_sp, true ) {}
264-
265- MOCK_METHOD2 (ReadMemory,
266- llvm::Expected<std::vector<uint8_t >>(lldb::addr_t addr,
267- size_t size));
268-
269- size_t ReadMemory (const Address &addr, void *dst, size_t dst_len,
270- Status &error, bool force_live_memory = false ,
271- lldb::addr_t *load_addr_ptr = nullptr ,
272- bool *did_read_live_memory = nullptr ) /* override*/ {
273- auto expected_memory = this ->ReadMemory (addr.GetOffset (), dst_len);
274- if (!expected_memory) {
275- llvm::consumeError (expected_memory.takeError ());
276- return 0 ;
277- }
278- const size_t bytes_read = expected_memory->size ();
279- assert (bytes_read <= dst_len);
280- std::memcpy (dst, expected_memory->data (), bytes_read);
281- return bytes_read;
282- }
283- };
284-
285316TEST (DWARFExpression, DW_OP_pick) {
286317 EXPECT_THAT_EXPECTED (Evaluate ({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0 }),
287318 ExpectScalar (0 ));
@@ -544,8 +575,12 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
544575 EXPECT_THAT_EXPECTED (Evaluate ({DW_OP_lit0, DW_OP_deref}), llvm::Failed ());
545576
546577 // Set up a mock process.
578+ MockMemory::Map memory = {
579+ {{0x4 , 4 }, {0x4 , 0x5 , 0x6 , 0x7 }},
580+ };
547581 TestContext test_ctx;
548- ASSERT_TRUE (CreateTestContext (&test_ctx, " i386-pc-linux" ));
582+ ASSERT_TRUE (
583+ CreateTestContext (&test_ctx, " i386-pc-linux" , {}, std::move (memory)));
549584
550585 ExecutionContext exe_ctx (test_ctx.process_sp );
551586 // Implicit location: *0x4.
@@ -1057,23 +1092,16 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {
10571092 using ::testing::Return;
10581093
10591094 // Set up a mock process.
1060- ArchSpec arch (" i386-pc-linux" );
1061- Platform::SetHostPlatform (
1062- platform_linux::PlatformLinux::CreateInstance (true , &arch));
1063- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance ();
1064- ASSERT_TRUE (debugger_sp);
1065- lldb::PlatformSP platform_sp;
1066- auto target_sp =
1067- std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp);
1068- ASSERT_TRUE (target_sp);
1069- ASSERT_TRUE (target_sp->GetArchitecture ().IsValid ());
1070-
1071- EXPECT_CALL (*target_sp, ReadMemory (0x40 , 1 ))
1072- .WillOnce (Return (ByMove (std::vector<uint8_t >{0x11 })));
1073- EXPECT_CALL (*target_sp, ReadMemory (0x50 , 1 ))
1074- .WillOnce (Return (ByMove (std::vector<uint8_t >{0x22 })));
1095+ TestContext test_ctx;
1096+ MockMemory::Map memory = {
1097+ {{0x40 , 1 }, {0x11 }},
1098+ {{0x50 , 1 }, {0x22 }},
1099+ };
1100+ ASSERT_TRUE (
1101+ CreateTestContext (&test_ctx, " i386-pc-linux" , {}, {}, std::move (memory)));
1102+ ASSERT_TRUE (test_ctx.target_sp ->GetArchitecture ().IsValid ());
10751103
1076- ExecutionContext exe_ctx (static_cast <lldb::TargetSP>( target_sp) , false );
1104+ ExecutionContext exe_ctx (test_ctx. target_sp , false );
10771105
10781106 uint8_t expr[] = {DW_OP_addr, 0x40 , 0x0 , 0x0 , 0x0 , DW_OP_piece, 1 ,
10791107 DW_OP_addr, 0x50 , 0x0 , 0x0 , 0x0 , DW_OP_piece, 1 };
@@ -1106,10 +1134,10 @@ class DWARFExpressionMockProcessTestWithAArch
11061134// / The expression DW_OP_breg22, 0, DW_OP_deref should produce that same value,
11071135// / without clearing the top byte 0xff.
11081136TEST_F (DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) {
1109- llvm::DenseMap<lldb::addr_t , lldb::addr_t > memory;
11101137 constexpr lldb::addr_t expected_value = ((0xffULL ) << 56 ) | 0xabcdefULL ;
11111138 constexpr lldb::addr_t addr = 42 ;
1112- memory[addr] = expected_value;
1139+ MockMemory::Map memory = {
1140+ {{addr, sizeof (addr)}, {0xef , 0xcd , 0xab , 0x00 , 0x00 , 0x00 , 0x00 , 0xff }}};
11131141
11141142 TestContext test_ctx;
11151143 ASSERT_TRUE (CreateTestContext (&test_ctx, " aarch64-pc-linux" ,
0 commit comments