| 1 | //===-- NativeProcessTestUtils.cpp ------------------------------*- C++ -*-===// |
| 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 | #ifndef LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H |
| 10 | #define LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H |
| 11 | |
| 12 | #include "lldb/Host/common/NativeProcessProtocol.h" |
| 13 | #include "llvm/Testing/Support/Error.h" |
| 14 | #include "gmock/gmock.h" |
| 15 | |
| 16 | using namespace lldb_private; |
| 17 | using namespace lldb; |
| 18 | using namespace testing; |
| 19 | |
| 20 | namespace lldb_private { |
| 21 | |
| 22 | class MockDelegate : public NativeProcessProtocol::NativeDelegate { |
| 23 | public: |
| 24 | MOCK_METHOD1(InitializeDelegate, void(NativeProcessProtocol *Process)); |
| 25 | MOCK_METHOD2(ProcessStateChanged, |
| 26 | void(NativeProcessProtocol *Process, StateType State)); |
| 27 | MOCK_METHOD1(DidExec, void(NativeProcessProtocol *Process)); |
| 28 | MOCK_METHOD2(NewSubprocessImpl, |
| 29 | void(NativeProcessProtocol *parent_process, |
| 30 | std::unique_ptr<NativeProcessProtocol> &child_process)); |
| 31 | // This is a hack to avoid MOCK_METHOD2 incompatibility with std::unique_ptr |
| 32 | // passed as value. |
| 33 | void NewSubprocess(NativeProcessProtocol *parent_process, |
| 34 | std::unique_ptr<NativeProcessProtocol> child_process) { |
| 35 | NewSubprocessImpl(gmock_a0: parent_process, gmock_a1&: child_process); |
| 36 | } |
| 37 | }; |
| 38 | |
| 39 | // NB: This class doesn't use the override keyword to avoid |
| 40 | // -Winconsistent-missing-override warnings from the compiler. The |
| 41 | // inconsistency comes from the overriding definitions in the MOCK_*** macros. |
| 42 | template <typename T> class MockProcess : public T { |
| 43 | public: |
| 44 | MockProcess(NativeProcessProtocol::NativeDelegate &Delegate, |
| 45 | const ArchSpec &Arch, lldb::pid_t Pid = 1) |
| 46 | : T(Pid, -1, Delegate), Arch(Arch) {} |
| 47 | |
| 48 | MOCK_METHOD1(Resume, Status(const ResumeActionList &ResumeActions)); |
| 49 | MOCK_METHOD0(Halt, Status()); |
| 50 | MOCK_METHOD0(Detach, Status()); |
| 51 | MOCK_METHOD1(Signal, Status(int Signo)); |
| 52 | MOCK_METHOD0(Kill, Status()); |
| 53 | MOCK_METHOD0(GetSharedLibraryInfoAddress, addr_t()); |
| 54 | MOCK_METHOD0(UpdateThreads, size_t()); |
| 55 | MOCK_CONST_METHOD0(GetAuxvData, |
| 56 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>()); |
| 57 | MOCK_METHOD2(GetLoadedModuleFileSpec, |
| 58 | Status(const char *ModulePath, FileSpec &Spec)); |
| 59 | MOCK_METHOD2(GetFileLoadAddress, |
| 60 | Status(const llvm::StringRef &FileName, addr_t &Addr)); |
| 61 | |
| 62 | const ArchSpec &GetArchitecture() const /*override*/ { return Arch; } |
| 63 | Status SetBreakpoint(lldb::addr_t Addr, uint32_t Size, |
| 64 | bool Hardware) /*override*/ { |
| 65 | if (Hardware) |
| 66 | return this->SetHardwareBreakpoint(Addr, Size); |
| 67 | else |
| 68 | return this->SetSoftwareBreakpoint(Addr, Size); |
| 69 | } |
| 70 | |
| 71 | // Redirect base class Read/Write Memory methods to functions whose signatures |
| 72 | // are more mock-friendly. |
| 73 | Status ReadMemory(addr_t Addr, void *Buf, size_t Size, |
| 74 | size_t &BytesRead) /*override*/ { |
| 75 | auto ExpectedMemory = this->ReadMemory(Addr, Size); |
| 76 | if (!ExpectedMemory) { |
| 77 | BytesRead = 0; |
| 78 | return Status::FromError(error: ExpectedMemory.takeError()); |
| 79 | } |
| 80 | BytesRead = ExpectedMemory->size(); |
| 81 | assert(BytesRead <= Size); |
| 82 | std::memcpy(dest: Buf, src: ExpectedMemory->data(), n: BytesRead); |
| 83 | return Status(); |
| 84 | } |
| 85 | |
| 86 | Status WriteMemory(addr_t Addr, const void *Buf, size_t Size, |
| 87 | size_t &BytesWritten) /*override*/ { |
| 88 | auto ExpectedBytes = this->WriteMemory( |
| 89 | Addr, llvm::ArrayRef(static_cast<const uint8_t *>(Buf), Size)); |
| 90 | if (!ExpectedBytes) { |
| 91 | BytesWritten = 0; |
| 92 | return Status::FromError(error: ExpectedBytes.takeError()); |
| 93 | } |
| 94 | BytesWritten = *ExpectedBytes; |
| 95 | return Status(); |
| 96 | } |
| 97 | |
| 98 | MOCK_METHOD2(ReadMemory, |
| 99 | llvm::Expected<std::vector<uint8_t>>(addr_t Addr, size_t Size)); |
| 100 | MOCK_METHOD2(WriteMemory, |
| 101 | llvm::Expected<size_t>(addr_t Addr, |
| 102 | llvm::ArrayRef<uint8_t> Data)); |
| 103 | |
| 104 | using T::GetSoftwareBreakpointTrapOpcode; |
| 105 | llvm::Expected<std::vector<uint8_t>> ReadMemoryWithoutTrap(addr_t Addr, |
| 106 | size_t Size) { |
| 107 | std::vector<uint8_t> Data(Size, 0); |
| 108 | size_t BytesRead; |
| 109 | Status ST = |
| 110 | T::ReadMemoryWithoutTrap(Addr, Data.data(), Data.size(), BytesRead); |
| 111 | if (ST.Fail()) |
| 112 | return ST.ToError(); |
| 113 | Data.resize(new_size: BytesRead); |
| 114 | return std::move(Data); |
| 115 | } |
| 116 | |
| 117 | private: |
| 118 | ArchSpec Arch; |
| 119 | }; |
| 120 | |
| 121 | class FakeMemory { |
| 122 | public: |
| 123 | FakeMemory(llvm::ArrayRef<uint8_t> Data, addr_t start_addr = 0) |
| 124 | : Data(Data), m_start_addr(start_addr) {} |
| 125 | |
| 126 | FakeMemory(const void *Data, size_t data_size, addr_t start_addr = 0) |
| 127 | : Data((const uint8_t *)Data, ((const uint8_t *)Data) + data_size), |
| 128 | m_start_addr(start_addr) {} |
| 129 | |
| 130 | llvm::Expected<std::vector<uint8_t>> Read(addr_t Addr, size_t Size) { |
| 131 | Addr -= m_start_addr; |
| 132 | if (Addr >= Data.size()) |
| 133 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 134 | S: "Address out of range." ); |
| 135 | Size = std::min(a: Size, b: Data.size() - (size_t)Addr); |
| 136 | auto Begin = std::next(x: Data.begin(), n: Addr); |
| 137 | return std::vector<uint8_t>(Begin, std::next(x: Begin, n: Size)); |
| 138 | } |
| 139 | |
| 140 | llvm::Expected<size_t> Write(addr_t Addr, llvm::ArrayRef<uint8_t> Chunk) { |
| 141 | Addr -= m_start_addr; |
| 142 | if (Addr >= Data.size()) |
| 143 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 144 | S: "Address out of range." ); |
| 145 | size_t Size = std::min(a: Chunk.size(), b: Data.size() - (size_t)Addr); |
| 146 | std::copy_n(first: Chunk.begin(), n: Size, result: &Data[Addr]); |
| 147 | return Size; |
| 148 | } |
| 149 | |
| 150 | private: |
| 151 | std::vector<uint8_t> Data; |
| 152 | addr_t m_start_addr; |
| 153 | }; |
| 154 | } // namespace lldb_private |
| 155 | |
| 156 | #endif |
| 157 | |