diff options
Diffstat (limited to 'gnu/llvm/lldb/unittests/Process')
20 files changed, 2490 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/unittests/Process/CMakeLists.txt b/gnu/llvm/lldb/unittests/Process/CMakeLists.txt new file mode 100644 index 00000000000..a4045692eb5 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(gdb-remote) +if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") + add_subdirectory(Linux) + add_subdirectory(POSIX) +endif() +add_subdirectory(minidump) diff --git a/gnu/llvm/lldb/unittests/Process/Linux/CMakeLists.txt b/gnu/llvm/lldb/unittests/Process/Linux/CMakeLists.txt new file mode 100644 index 00000000000..3b55b5c8430 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/Linux/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/Linux) + +add_lldb_unittest(ProcessorTraceTest + ProcessorTraceTest.cpp + + LINK_LIBS + lldbPluginProcessLinux + )
\ No newline at end of file diff --git a/gnu/llvm/lldb/unittests/Process/Linux/ProcessorTraceTest.cpp b/gnu/llvm/lldb/unittests/Process/Linux/ProcessorTraceTest.cpp new file mode 100644 index 00000000000..d3165a1db9c --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/Linux/ProcessorTraceTest.cpp @@ -0,0 +1,147 @@ +//===-- ProcessorTraceMonitorTest.cpp ------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "ProcessorTrace.h" +#include "llvm/ADT/ArrayRef.h" + + +using namespace lldb_private; +using namespace process_linux; + +size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf, + size_t cyc_buf_size, size_t cyc_start, + size_t offset) { + llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf), + buf_size); + llvm::MutableArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf), + cyc_buf_size); + ProcessorTraceMonitor::ReadCyclicBuffer(dst, src, cyc_start, offset); + return dst.size(); +} + +TEST(CyclicBuffer, EdgeCases) { + size_t bytes_read; + uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; + + // We will always leave the last bytes untouched + // so that string comparisions work. + char smaller_buffer[4] = {}; + + // empty buffer to read into + bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer, + sizeof(cyclic_buffer), 3, 0); + ASSERT_EQ(0u, bytes_read); + + // empty cyclic buffer + bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), + cyclic_buffer, 0, 3, 0); + ASSERT_EQ(0u, bytes_read); + + // bigger offset + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), + cyclic_buffer, sizeof(cyclic_buffer), 3, 6); + ASSERT_EQ(0u, bytes_read); + + // wrong offset + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), + cyclic_buffer, sizeof(cyclic_buffer), 3, 7); + ASSERT_EQ(0u, bytes_read); + + // wrong start + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), + cyclic_buffer, sizeof(cyclic_buffer), 3, 7); + ASSERT_EQ(0u, bytes_read); +} + +TEST(CyclicBuffer, EqualSizeBuffer) { + size_t bytes_read = 0; + uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; + + char cyclic[] = "cyclic"; + for (size_t i = 0; i < sizeof(cyclic); i++) { + // We will always leave the last bytes untouched + // so that string comparisions work. + char equal_size_buffer[7] = {}; + bytes_read = + ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer), + cyclic_buffer, sizeof(cyclic_buffer), 3, i); + ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read); + ASSERT_STREQ(equal_size_buffer, (cyclic + i)); + } +} + +TEST(CyclicBuffer, SmallerSizeBuffer) { + size_t bytes_read; + uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; + + // We will always leave the last bytes untouched + // so that string comparisions work. + char smaller_buffer[4] = {}; + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 0); + ASSERT_EQ(3u, bytes_read); + ASSERT_STREQ(smaller_buffer, "cyc"); + + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 1); + ASSERT_EQ(3u, bytes_read); + ASSERT_STREQ(smaller_buffer, "ycl"); + + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 2); + ASSERT_EQ(3u, bytes_read); + ASSERT_STREQ(smaller_buffer, "cli"); + + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 3); + ASSERT_EQ(3u, bytes_read); + ASSERT_STREQ(smaller_buffer, "lic"); + + { + char smaller_buffer[4] = {}; + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 4); + ASSERT_EQ(2u, bytes_read); + ASSERT_STREQ(smaller_buffer, "ic"); + } + { + char smaller_buffer[4] = {}; + bytes_read = + ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, 5); + ASSERT_EQ(1u, bytes_read); + ASSERT_STREQ(smaller_buffer, "c"); + } +} + +TEST(CyclicBuffer, BiggerSizeBuffer) { + size_t bytes_read = 0; + uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; + + char cyclic[] = "cyclic"; + for (size_t i = 0; i < sizeof(cyclic); i++) { + // We will always leave the last bytes untouched + // so that string comparisions work. + char bigger_buffer[10] = {}; + bytes_read = + ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1), + cyclic_buffer, sizeof(cyclic_buffer), 3, i); + ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read); + ASSERT_STREQ(bigger_buffer, (cyclic + i)); + } +} diff --git a/gnu/llvm/lldb/unittests/Process/POSIX/CMakeLists.txt b/gnu/llvm/lldb/unittests/Process/POSIX/CMakeLists.txt new file mode 100644 index 00000000000..53318d908da --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/POSIX/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Process/POSIX) + +add_lldb_unittest(ProcessPOSIXTest + NativeProcessELFTest.cpp + + LINK_LIBS + lldbPluginProcessPOSIX + ) diff --git a/gnu/llvm/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp b/gnu/llvm/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp new file mode 100644 index 00000000000..9e91464c441 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/POSIX/NativeProcessELFTest.cpp @@ -0,0 +1,155 @@ +//===-- NativeProcessELFTest.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TestingSupport/Host/NativeProcessTestUtils.h" + +#include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "Plugins/Process/Utility/AuxVector.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "gmock/gmock.h" + +using namespace lldb_private; +using namespace lldb; +using namespace testing; + +namespace { +class MockProcessELF : public MockProcess<NativeProcessELF> { +public: + using MockProcess::MockProcess; + using NativeProcessELF::GetAuxValue; + using NativeProcessELF::GetELFImageInfoAddress; +}; + +std::unique_ptr<llvm::MemoryBuffer> CreateAuxvData( + MockProcessELF &process, + llvm::ArrayRef<std::pair<AuxVector::EntryType, uint32_t>> auxv_data) { + auto addr_size = process.GetAddressByteSize(); + DataBufferSP buffer_sp( + new DataBufferHeap(auxv_data.size() * addr_size * 2, 0)); + DataEncoder encoder(buffer_sp, process.GetByteOrder(), addr_size); + uint32_t offset = 0; + for (auto &pair : auxv_data) { + offset = encoder.PutAddress(offset, pair.first); + offset = encoder.PutAddress(offset, pair.second); + } + return llvm::MemoryBuffer::getMemBufferCopy( + llvm::toStringRef(buffer_sp->GetData()), ""); +} + +} // namespace + +TEST(NativeProcessELFTest, GetAuxValue) { + NiceMock<MockDelegate> DummyDelegate; + MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux")); + + uint64_t phdr_addr = 0x42; + auto auxv_buffer = CreateAuxvData( + process, {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr)}); + EXPECT_CALL(process, GetAuxvData()) + .WillOnce(Return(ByMove(std::move(auxv_buffer)))); + + ASSERT_EQ(phdr_addr, process.GetAuxValue(AuxVector::AUXV_AT_PHDR)); +} + +TEST(NativeProcessELFTest, GetELFImageInfoAddress) { + NiceMock<MockDelegate> DummyDelegate; + MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux")); + + uint32_t load_base = 0x1000; + uint32_t info_addr = 0x3741; + uint32_t phdr_addr = load_base + sizeof(llvm::ELF::Elf32_Ehdr); + + auto auxv_buffer = CreateAuxvData( + process, + {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr), + std::make_pair(AuxVector::AUXV_AT_PHENT, sizeof(llvm::ELF::Elf32_Phdr)), + std::make_pair(AuxVector::AUXV_AT_PHNUM, 2)}); + EXPECT_CALL(process, GetAuxvData()) + .WillOnce(Return(ByMove(std::move(auxv_buffer)))); + + // We're going to set up a fake memory with 2 program headers and 1 entry in + // the dynamic section. For simplicity sake they will be contiguous in memory. + struct MemoryContents { + llvm::ELF::Elf32_Phdr phdr_load; + llvm::ELF::Elf32_Phdr phdr_dynamic; + llvm::ELF::Elf32_Dyn dyn_debug; + } MC; + // Setup the 2 program header entries + MC.phdr_load.p_type = llvm::ELF::PT_PHDR; + MC.phdr_load.p_vaddr = phdr_addr - load_base; + + MC.phdr_dynamic.p_type = llvm::ELF::PT_DYNAMIC; + MC.phdr_dynamic.p_vaddr = + (phdr_addr + 2 * sizeof(llvm::ELF::Elf32_Phdr)) - load_base; + MC.phdr_dynamic.p_memsz = sizeof(llvm::ELF::Elf32_Dyn); + + // Setup the single entry in the .dynamic section + MC.dyn_debug.d_tag = llvm::ELF::DT_DEBUG; + MC.dyn_debug.d_un.d_ptr = info_addr; + + FakeMemory M(&MC, sizeof(MC), phdr_addr); + EXPECT_CALL(process, ReadMemory(_, _)) + .WillRepeatedly(Invoke(&M, &FakeMemory::Read)); + + lldb::addr_t elf_info_addr = process.GetELFImageInfoAddress< + llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>(); + + // Read the address at the elf_info_addr location to make sure we're reading + // the correct one. + lldb::offset_t info_addr_offset = elf_info_addr - phdr_addr; + DataExtractor mem_extractor(&MC, sizeof(MC), process.GetByteOrder(), + process.GetAddressByteSize()); + ASSERT_EQ(mem_extractor.GetAddress(&info_addr_offset), info_addr); +} + +TEST(NativeProcessELFTest, GetELFImageInfoAddress_NoDebugEntry) { + NiceMock<MockDelegate> DummyDelegate; + MockProcessELF process(DummyDelegate, ArchSpec("i386-pc-linux")); + + uint32_t phdr_addr = sizeof(llvm::ELF::Elf32_Ehdr); + + auto auxv_buffer = CreateAuxvData( + process, + {std::make_pair(AuxVector::AUXV_AT_PHDR, phdr_addr), + std::make_pair(AuxVector::AUXV_AT_PHENT, sizeof(llvm::ELF::Elf32_Phdr)), + std::make_pair(AuxVector::AUXV_AT_PHNUM, 2)}); + EXPECT_CALL(process, GetAuxvData()) + .WillOnce(Return(ByMove(std::move(auxv_buffer)))); + + // We're going to set up a fake memory with 2 program headers and 1 entry in + // the dynamic section. For simplicity sake they will be contiguous in memory. + struct MemoryContents { + llvm::ELF::Elf32_Phdr phdr_load; + llvm::ELF::Elf32_Phdr phdr_dynamic; + llvm::ELF::Elf32_Dyn dyn_notdebug; + } MC; + // Setup the 2 program header entries + MC.phdr_load.p_type = llvm::ELF::PT_PHDR; + MC.phdr_load.p_vaddr = phdr_addr; + + MC.phdr_dynamic.p_type = llvm::ELF::PT_DYNAMIC; + MC.phdr_dynamic.p_vaddr = (phdr_addr + 2 * sizeof(llvm::ELF::Elf32_Phdr)); + MC.phdr_dynamic.p_memsz = sizeof(llvm::ELF::Elf32_Dyn); + + // Setup the single entry in the .dynamic section + MC.dyn_notdebug.d_tag = llvm::ELF::DT_NULL; + + FakeMemory M(&MC, sizeof(MC), phdr_addr); + EXPECT_CALL(process, ReadMemory(_, _)) + .WillRepeatedly(Invoke(&M, &FakeMemory::Read)); + + lldb::addr_t elf_info_addr = process.GetELFImageInfoAddress< + llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>(); + + ASSERT_EQ(elf_info_addr, LLDB_INVALID_ADDRESS); +} diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/CMakeLists.txt b/gnu/llvm/lldb/unittests/Process/gdb-remote/CMakeLists.txt new file mode 100644 index 00000000000..abb30e022e4 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/CMakeLists.txt @@ -0,0 +1,19 @@ +add_lldb_unittest(ProcessGdbRemoteTests + GDBRemoteClientBaseTest.cpp + GDBRemoteCommunicationClientTest.cpp + GDBRemoteCommunicationServerTest.cpp + GDBRemoteCommunicationTest.cpp + GDBRemoteTestUtils.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbPluginPlatformMacOSX + lldbPluginProcessUtility + lldbPluginProcessGDBRemote + + LLVMTestingSupport + + LINK_COMPONENTS + Support + ) diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp new file mode 100644 index 00000000000..d56e9cf81bc --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp @@ -0,0 +1,361 @@ +//===-- GDBRemoteClientBaseTest.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include <future> + +#include "GDBRemoteTestUtils.h" + +#include "Plugins/Process/Utility/LinuxSignals.h" +#include "Plugins/Process/gdb-remote/GDBRemoteClientBase.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" +#include "lldb/Utility/GDBRemote.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace lldb; +typedef GDBRemoteCommunication::PacketResult PacketResult; + +namespace { + +struct MockDelegate : public GDBRemoteClientBase::ContinueDelegate { + std::string output; + std::string misc_data; + unsigned stop_reply_called = 0; + std::vector<std::string> structured_data_packets; + + void HandleAsyncStdout(llvm::StringRef out) override { output += out; } + void HandleAsyncMisc(llvm::StringRef data) override { misc_data += data; } + void HandleStopReply() override { ++stop_reply_called; } + + void HandleAsyncStructuredDataPacket(llvm::StringRef data) override { + structured_data_packets.push_back(data); + } +}; + +struct TestClient : public GDBRemoteClientBase { + TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") { + m_send_acks = false; + } +}; + +class GDBRemoteClientBaseTest : public GDBRemoteTest { +public: + void SetUp() override { + ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server), + llvm::Succeeded()); + ASSERT_EQ(TestClient::eBroadcastBitRunPacketSent, + listener_sp->StartListeningForEvents( + &client, TestClient::eBroadcastBitRunPacketSent)); + } + +protected: + TestClient client; + MockServer server; + MockDelegate delegate; + ListenerSP listener_sp = Listener::MakeListener("listener"); + + StateType SendCPacket(StringExtractorGDBRemote &response) { + return client.SendContinuePacketAndWaitForResponse(delegate, LinuxSignals(), + "c", response); + } + + void WaitForRunEvent() { + EventSP event_sp; + listener_sp->GetEventForBroadcasterWithType( + &client, TestClient::eBroadcastBitRunPacketSent, event_sp, llvm::None); + } +}; + +} // end anonymous namespace + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndWait) { + StringExtractorGDBRemote response; + + // Continue. The inferior will stop with a signal. + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + // Continue. The inferior will exit. + ASSERT_EQ(PacketResult::Success, server.SendPacket("W01")); + ASSERT_EQ(eStateExited, SendCPacket(response)); + ASSERT_EQ("W01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + // Continue. The inferior will get killed. + ASSERT_EQ(PacketResult::Success, server.SendPacket("X01")); + ASSERT_EQ(eStateExited, SendCPacket(response)); + ASSERT_EQ("X01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) { + StringExtractorGDBRemote continue_response, response; + + // SendAsyncSignal should do nothing when we are not running. + ASSERT_FALSE(client.SendAsyncSignal(0x47)); + + // Continue. After the run packet is sent, send an async signal. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + std::future<bool> async_result = std::async( + std::launch::async, [&] { return client.SendAsyncSignal(0x47); }); + + // First we'll get interrupted. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + + // Then we get the signal packet. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("C47", response.GetStringRef()); + ASSERT_TRUE(async_result.get()); + + // And we report back a signal stop. + ASSERT_EQ(PacketResult::Success, server.SendPacket("T47")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T47", continue_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncPacket) { + StringExtractorGDBRemote continue_response, async_response, response; + const bool send_async = true; + + // Continue. After the run packet is sent, send an async packet. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + // Sending without async enabled should fail. + ASSERT_EQ( + PacketResult::ErrorSendFailed, + client.SendPacketAndWaitForResponse("qTest1", response, !send_async)); + + std::future<PacketResult> async_result = std::async(std::launch::async, [&] { + return client.SendPacketAndWaitForResponse("qTest2", async_response, + send_async); + }); + + // First we'll get interrupted. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + + // Then we get the async packet. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("qTest2", response.GetStringRef()); + + // Send the response and receive it. + ASSERT_EQ(PacketResult::Success, server.SendPacket("QTest2")); + ASSERT_EQ(PacketResult::Success, async_result.get()); + ASSERT_EQ("QTest2", async_response.GetStringRef()); + + // And we get resumed again. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T01", continue_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) { + StringExtractorGDBRemote continue_response, response; + + // Interrupt should do nothing when we're not running. + ASSERT_FALSE(client.Interrupt()); + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return client.Interrupt(); }); + + // We get interrupted. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + + // And that's it. + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T13", continue_response.GetStringRef()); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndLateInterrupt) { + StringExtractorGDBRemote continue_response, response; + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return client.Interrupt(); }); + + // However, the target stops due to a different reason than the original + // interrupt. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T01", continue_response.GetStringRef()); + ASSERT_TRUE(async_result.get()); + + // The subsequent continue packet should work normally. + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) { + StringExtractorGDBRemote continue_response, async_response, response; + const bool send_async = true; + + // Interrupt should do nothing when we're not running. + ASSERT_FALSE(client.Interrupt()); + + // Continue. After the run packet is sent, send an async signal. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + std::future<bool> interrupt_result = + std::async(std::launch::async, [&] { return client.Interrupt(); }); + + // We get interrupted. We'll send two packets to simulate a buggy stub. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T13")); + + // We should stop. + ASSERT_EQ(eStateStopped, continue_state.get()); + ASSERT_EQ("T13", continue_response.GetStringRef()); + ASSERT_TRUE(interrupt_result.get()); + + // Packet stream should remain synchronized. + std::future<PacketResult> send_result = std::async(std::launch::async, [&] { + return client.SendPacketAndWaitForResponse("qTest", async_response, + !send_async); + }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("qTest", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.SendPacket("QTest")); + ASSERT_EQ(PacketResult::Success, send_result.get()); + ASSERT_EQ("QTest", async_response.GetStringRef()); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateInterface) { + StringExtractorGDBRemote response; + + // Continue. We'll have the server send a bunch of async packets before it + // stops. + ASSERT_EQ(PacketResult::Success, server.SendPacket("O4142")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("Apro")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O4344")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("Afile")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + + EXPECT_EQ("ABCD", delegate.output); + EXPECT_EQ("profile", delegate.misc_data); + EXPECT_EQ(1u, delegate.stop_reply_called); +} + +TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateStructuredDataReceipt) { + // Build the plain-text version of the JSON data we will have the + // server send. + const std::string json_payload = + "{ \"type\": \"MyFeatureType\", " + " \"elements\": [ \"entry1\", \"entry2\" ] }"; + const std::string json_packet = "JSON-async:" + json_payload; + + // Escape it properly for transit. + StreamGDBRemote stream; + stream.PutEscapedBytes(json_packet.c_str(), json_packet.length()); + stream.Flush(); + + StringExtractorGDBRemote response; + + // Send async structured data packet, then stop. + ASSERT_EQ(PacketResult::Success, server.SendPacket(stream.GetData())); + ASSERT_EQ(PacketResult::Success, server.SendPacket("T01")); + ASSERT_EQ(eStateStopped, SendCPacket(response)); + ASSERT_EQ("T01", response.GetStringRef()); + ASSERT_EQ(1ul, delegate.structured_data_packets.size()); + + // Verify the packet contents. It should have been unescaped upon packet + // reception. + ASSERT_EQ(json_packet, delegate.structured_data_packets[0]); +} + +TEST_F(GDBRemoteClientBaseTest, InterruptNoResponse) { + StringExtractorGDBRemote continue_response, response; + + // Continue. After the run packet is sent, send an interrupt. + std::future<StateType> continue_state = std::async( + std::launch::async, [&] { return SendCPacket(continue_response); }); + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("c", response.GetStringRef()); + WaitForRunEvent(); + + std::future<bool> async_result = + std::async(std::launch::async, [&] { return client.Interrupt(); }); + + // We get interrupted, but we don't send a stop packet. + ASSERT_EQ(PacketResult::Success, server.GetPacket(response)); + ASSERT_EQ("\x03", response.GetStringRef()); + + // The functions should still terminate (after a timeout). + ASSERT_TRUE(async_result.get()); + ASSERT_EQ(eStateInvalid, continue_state.get()); +} + +TEST_F(GDBRemoteClientBaseTest, SendPacketAndReceiveResponseWithOutputSupport) { + StringExtractorGDBRemote response; + StreamString command_output; + + ASSERT_EQ(PacketResult::Success, server.SendPacket("O")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O48656c6c6f2c")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O20")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("O776f726c64")); + ASSERT_EQ(PacketResult::Success, server.SendPacket("OK")); + + PacketResult result = client.SendPacketAndReceiveResponseWithOutputSupport( + "qRcmd,test", response, true, + [&command_output](llvm::StringRef output) { command_output << output; }); + + ASSERT_EQ(PacketResult::Success, result); + ASSERT_EQ("OK", response.GetStringRef()); + ASSERT_EQ("Hello, world", command_output.GetString().str()); +} diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp new file mode 100644 index 00000000000..e52f3782437 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -0,0 +1,554 @@ +//===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "GDBRemoteTestUtils.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/XML.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/TraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Testing/Support/Error.h" +#include "gmock/gmock.h" +#include <future> + +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace lldb; +using namespace llvm; + +namespace { + +typedef GDBRemoteCommunication::PacketResult PacketResult; + +struct TestClient : public GDBRemoteCommunicationClient { + TestClient() { m_send_acks = false; } +}; + +void Handle_QThreadSuffixSupported(MockServer &server, bool supported) { + StringExtractorGDBRemote request; + ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); + ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef()); + if (supported) + ASSERT_EQ(PacketResult::Success, server.SendOKResponse()); + else + ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr)); +} + +void HandlePacket(MockServer &server, + const testing::Matcher<const std::string &> &expected, + StringRef response) { + StringExtractorGDBRemote request; + ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); + ASSERT_THAT(request.GetStringRef(), expected); + ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); +} + +uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'}; +std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f"; +uint8_t one_register[] = {'A', 'B', 'C', 'D'}; +std::string one_register_hex = "41424344"; + +} // end anonymous namespace + +class GDBRemoteCommunicationClientTest : public GDBRemoteTest { +public: + void SetUp() override { + ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server), + llvm::Succeeded()); + } + +protected: + TestClient client; + MockServer server; +}; + +TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> write_result = std::async(std::launch::async, [&] { + return client.WriteRegister(tid, reg_num, one_register); + }); + + Handle_QThreadSuffixSupported(server, true); + + HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK"); + ASSERT_TRUE(write_result.get()); + + write_result = std::async(std::launch::async, [&] { + return client.WriteAllRegisters(tid, all_registers); + }); + + HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK"); + ASSERT_TRUE(write_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> write_result = std::async(std::launch::async, [&] { + return client.WriteRegister(tid, reg_num, one_register); + }); + + Handle_QThreadSuffixSupported(server, false); + HandlePacket(server, "Hg47", "OK"); + HandlePacket(server, "P4=" + one_register_hex, "OK"); + ASSERT_TRUE(write_result.get()); + + write_result = std::async(std::launch::async, [&] { + return client.WriteAllRegisters(tid, all_registers); + }); + + HandlePacket(server, "G" + all_registers_hex, "OK"); + ASSERT_TRUE(write_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { + const lldb::tid_t tid = 0x47; + const uint32_t reg_num = 4; + std::future<bool> async_result = std::async( + std::launch::async, [&] { return client.GetpPacketSupported(tid); }); + Handle_QThreadSuffixSupported(server, true); + HandlePacket(server, "p0;thread:0047;", one_register_hex); + ASSERT_TRUE(async_result.get()); + + std::future<DataBufferSP> read_result = std::async( + std::launch::async, [&] { return client.ReadRegister(tid, reg_num); }); + HandlePacket(server, "p4;thread:0047;", "41424344"); + auto buffer_sp = read_result.get(); + ASSERT_TRUE(bool(buffer_sp)); + ASSERT_EQ(0, + memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register)); + + read_result = std::async(std::launch::async, + [&] { return client.ReadAllRegisters(tid); }); + HandlePacket(server, "g;thread:0047;", all_registers_hex); + buffer_sp = read_result.get(); + ASSERT_TRUE(bool(buffer_sp)); + ASSERT_EQ(0, + memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); +} + +TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { + const lldb::tid_t tid = 0x47; + uint32_t save_id; + std::future<bool> async_result = std::async(std::launch::async, [&] { + return client.SaveRegisterState(tid, save_id); + }); + Handle_QThreadSuffixSupported(server, false); + HandlePacket(server, "Hg47", "OK"); + HandlePacket(server, "QSaveRegisterState", "1"); + ASSERT_TRUE(async_result.get()); + EXPECT_EQ(1u, save_id); + + async_result = std::async(std::launch::async, [&] { + return client.RestoreRegisterState(tid, save_id); + }); + HandlePacket(server, "QRestoreRegisterState:1", "OK"); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { + const lldb::tid_t tid = 0x47; + std::future<bool> async_result = std::async( + std::launch::async, [&] { return client.SyncThreadState(tid); }); + HandlePacket(server, "qSyncThreadStateSupported", "OK"); + HandlePacket(server, "QSyncThreadState:0047;", "OK"); + ASSERT_TRUE(async_result.get()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { + llvm::Triple triple("i386-pc-linux"); + + FileSpec file_specs[] = { + FileSpec("/foo/bar.so", FileSpec::Style::posix), + FileSpec("/foo/baz.so", FileSpec::Style::posix), + + // This is a bit dodgy but we currently depend on GetModulesInfo not + // performing denormalization. It can go away once the users + // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for + // the FileSpecs they create. + FileSpec("/foo/baw.so", FileSpec::Style::windows), + }; + std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result = + std::async(std::launch::async, + [&] { return client.GetModulesInfo(file_specs, triple); }); + HandlePacket( + server, "jModulesInfo:[" + R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)" + R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)" + R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])", + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); + + auto result = async_result.get(); + ASSERT_TRUE(result.hasValue()); + ASSERT_EQ(1u, result->size()); + EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); + EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); + EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNO", 16), + result.getValue()[0].GetUUID()); + EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); + EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo_UUID20) { + llvm::Triple triple("i386-pc-linux"); + + FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix); + std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result = + std::async(std::launch::async, + [&] { return client.GetModulesInfo(file_spec, triple); }); + HandlePacket( + server, + "jModulesInfo:[" + R"({"file":"/foo/bar.so","triple":"i386-pc-linux"}])", + R"([{"uuid":"404142434445464748494a4b4c4d4e4f50515253","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); + + auto result = async_result.get(); + ASSERT_TRUE(result.hasValue()); + ASSERT_EQ(1u, result->size()); + EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); + EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); + EXPECT_EQ(UUID::fromData("@ABCDEFGHIJKLMNOPQRS", 20), + result.getValue()[0].GetUUID()); + EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); + EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { + llvm::Triple triple("i386-pc-linux"); + FileSpec file_spec("/foo/bar.so", FileSpec::Style::posix); + + const char *invalid_responses[] = { + // no UUID + R"([{"triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])", + // invalid UUID + R"([{"uuid":"XXXXXX","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])", + // no triple + R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)" + R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])", + // no file_path + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_offset":0,"file_size":1234}]])", + // no file_offset + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_size":1234}]])", + // no file_size + R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" + R"("file_path":"/foo/bar.so","file_offset":0}]])", + }; + + for (const char *response : invalid_responses) { + std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result = + std::async(std::launch::async, + [&] { return client.GetModulesInfo(file_spec, triple); }); + HandlePacket( + server, + R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])", + response); + + auto result = async_result.get(); + ASSERT_TRUE(result); + ASSERT_EQ(0u, result->size()) << "response was: " << response; + } +} + +TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { + std::thread server_thread([this] { + for (;;) { + StringExtractorGDBRemote request; + PacketResult result = server.GetPacket(request); + if (result == PacketResult::ErrorDisconnected) + return; + ASSERT_EQ(PacketResult::Success, result); + StringRef ref = request.GetStringRef(); + ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:")); + int size; + ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref; + std::string response(size, 'X'); + ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); + } + }); + + StreamString ss; + client.TestPacketSpeed(10, 32, 32, 4096, true, ss); + client.Disconnect(); + server_thread.join(); + + GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData(); + auto object_sp = StructuredData::ParseJSON(ss.GetString()); + ASSERT_TRUE(bool(object_sp)); + auto dict_sp = object_sp->GetAsDictionary(); + ASSERT_TRUE(bool(dict_sp)); + + object_sp = dict_sp->GetValueForKey("packet_speeds"); + ASSERT_TRUE(bool(object_sp)); + dict_sp = object_sp->GetAsDictionary(); + ASSERT_TRUE(bool(dict_sp)); + + int num_packets; + ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets)) + << ss.GetString(); + ASSERT_EQ(10, num_packets); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { + std::future<Status> result = std::async(std::launch::async, [&] { + return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11}); + }); + + HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK"); + EXPECT_TRUE(result.get().Success()); + + result = std::async(std::launch::async, [&] { + return client.SendSignalsToIgnore(std::vector<int32_t>()); + }); + + HandlePacket(server, "QPassSignals:", "OK"); + EXPECT_TRUE(result.get().Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { + const lldb::addr_t addr = 0xa000; + MemoryRegionInfo region_info; + std::future<Status> result = std::async(std::launch::async, [&] { + return client.GetMemoryRegionInfo(addr, region_info); + }); + + HandlePacket(server, + "qMemoryRegionInfo:a000", + "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;"); + if (XMLDocument::XMLEnabled()) { + // In case we have XML support, this will also do a "qXfer:memory-map". + // Preceeded by a query for supported extensions. Pretend we don't support + // that. + HandlePacket(server, testing::StartsWith("qSupported:"), ""); + } + EXPECT_TRUE(result.get().Success()); + EXPECT_EQ(addr, region_info.GetRange().GetRangeBase()); + EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize()); + EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable()); + EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable()); + EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable()); + EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef()); +} + +TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { + const lldb::addr_t addr = 0x4000; + MemoryRegionInfo region_info; + std::future<Status> result = std::async(std::launch::async, [&] { + return client.GetMemoryRegionInfo(addr, region_info); + }); + + HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;"); + if (XMLDocument::XMLEnabled()) { + // In case we have XML support, this will also do a "qXfer:memory-map". + // Preceeded by a query for supported extensions. Pretend we don't support + // that. + HandlePacket(server, testing::StartsWith("qSupported:"), ""); + } + EXPECT_FALSE(result.get().Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) { + TraceOptions options; + Status error; + + options.setType(lldb::TraceType::eTraceTypeProcessorTrace); + options.setMetaDataBufferSize(8192); + options.setTraceBufferSize(8192); + options.setThreadID(0x23); + + StructuredData::DictionarySP custom_params = + std::make_shared<StructuredData::Dictionary>(); + custom_params->AddStringItem("tracetech", "intel-pt"); + custom_params->AddIntegerItem("psb", 0x01); + + options.setTraceParams(custom_params); + + std::future<lldb::user_id_t> result = std::async(std::launch::async, [&] { + return client.SendStartTracePacket(options, error); + }); + + // Since the line is exceeding 80 characters. + std::string expected_packet1 = + R"(jTraceStart:{"buffersize":8192,"metabuffersize":8192,"params":)"; + std::string expected_packet2 = + R"({"psb":1,"tracetech":"intel-pt"},"threadid":35,"type":1})"; + HandlePacket(server, (expected_packet1 + expected_packet2), "1"); + ASSERT_TRUE(error.Success()); + ASSERT_EQ(result.get(), 1u); + + error.Clear(); + result = std::async(std::launch::async, [&] { + return client.SendStartTracePacket(options, error); + }); + + HandlePacket(server, (expected_packet1 + expected_packet2), "E23"); + ASSERT_EQ(result.get(), LLDB_INVALID_UID); + ASSERT_FALSE(error.Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) { + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + std::future<Status> result = std::async(std::launch::async, [&] { + return client.SendStopTracePacket(trace_id, thread_id); + }); + + const char *expected_packet = R"(jTraceStop:{"threadid":35,"traceid":3})"; + HandlePacket(server, expected_packet, "OK"); + ASSERT_TRUE(result.get().Success()); + + result = std::async(std::launch::async, [&] { + return client.SendStopTracePacket(trace_id, thread_id); + }); + + HandlePacket(server, expected_packet, "E23"); + ASSERT_FALSE(result.get().Success()); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) { + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + uint8_t buf[32] = {}; + llvm::MutableArrayRef<uint8_t> buffer(buf, 32); + size_t offset = 0; + + std::future<Status> result = std::async(std::launch::async, [&] { + return client.SendGetDataPacket(trace_id, thread_id, buffer, offset); + }); + + std::string expected_packet1 = + R"(jTraceBufferRead:{"buffersize":32,"offset":0,"threadid":35,)"; + std::string expected_packet2 = R"("traceid":3})"; + HandlePacket(server, expected_packet1+expected_packet2, "123456"); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(buffer.size(), 3u); + ASSERT_EQ(buf[0], 0x12); + ASSERT_EQ(buf[1], 0x34); + ASSERT_EQ(buf[2], 0x56); + + llvm::MutableArrayRef<uint8_t> buffer2(buf, 32); + result = std::async(std::launch::async, [&] { + return client.SendGetDataPacket(trace_id, thread_id, buffer2, offset); + }); + + HandlePacket(server, expected_packet1+expected_packet2, "E23"); + ASSERT_FALSE(result.get().Success()); + ASSERT_EQ(buffer2.size(), 0u); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) { + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + + uint8_t buf[32] = {}; + llvm::MutableArrayRef<uint8_t> buffer(buf, 32); + size_t offset = 0; + + std::future<Status> result = std::async(std::launch::async, [&] { + return client.SendGetMetaDataPacket(trace_id, thread_id, buffer, offset); + }); + + std::string expected_packet1 = + R"(jTraceMetaRead:{"buffersize":32,"offset":0,"threadid":35,)"; + std::string expected_packet2 = R"("traceid":3})"; + HandlePacket(server, expected_packet1+expected_packet2, "123456"); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(buffer.size(), 3u); + ASSERT_EQ(buf[0], 0x12); + ASSERT_EQ(buf[1], 0x34); + ASSERT_EQ(buf[2], 0x56); + + llvm::MutableArrayRef<uint8_t> buffer2(buf, 32); + result = std::async(std::launch::async, [&] { + return client.SendGetMetaDataPacket(trace_id, thread_id, buffer2, offset); + }); + + HandlePacket(server, expected_packet1+expected_packet2, "E23"); + ASSERT_FALSE(result.get().Success()); + ASSERT_EQ(buffer2.size(), 0u); +} + +TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) { + lldb::tid_t thread_id = 0x23; + lldb::user_id_t trace_id = 3; + TraceOptions options; + options.setThreadID(thread_id); + + std::future<Status> result = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + const char *expected_packet = + R"(jTraceConfigRead:{"threadid":35,"traceid":3})"; + std::string response1 = + R"({"buffersize":8192,"params":{"psb":1,"tracetech":"intel-pt"})"; + std::string response2 = R"(],"metabuffersize":8192,"threadid":35,"type":1}])"; + HandlePacket(server, expected_packet, response1+response2); + ASSERT_TRUE(result.get().Success()); + ASSERT_EQ(options.getTraceBufferSize(), 8192u); + ASSERT_EQ(options.getMetaDataBufferSize(), 8192u); + ASSERT_EQ(options.getType(), 1); + + auto custom_params = options.getTraceParams(); + + uint64_t psb_value; + llvm::StringRef trace_tech_value; + + ASSERT_TRUE(custom_params); + ASSERT_EQ(custom_params->GetType(), eStructuredDataTypeDictionary); + ASSERT_TRUE(custom_params->GetValueForKeyAsInteger("psb", psb_value)); + ASSERT_EQ(psb_value, 1u); + ASSERT_TRUE( + custom_params->GetValueForKeyAsString("tracetech", trace_tech_value)); + ASSERT_STREQ(trace_tech_value.data(), "intel-pt"); + + // Checking error response. + std::future<Status> result2 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + HandlePacket(server, expected_packet, "E23"); + ASSERT_FALSE(result2.get().Success()); + + // Wrong JSON as response. + std::future<Status> result3 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + std::string incorrect_json1 = + R"("buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})"; + std::string incorrect_json2 = + R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; + HandlePacket(server, expected_packet, incorrect_json1+incorrect_json2); + ASSERT_FALSE(result3.get().Success()); + + // Wrong JSON as custom_params. + std::future<Status> result4 = std::async(std::launch::async, [&] { + return client.SendGetTraceConfigPacket(trace_id, options); + }); + + std::string incorrect_custom_params1 = + R"({"buffersize" : 8192,"params" : "psb" : 1,"tracetech" : "intel-pt"})"; + std::string incorrect_custom_params2 = + R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; + HandlePacket(server, expected_packet, incorrect_custom_params1+ + incorrect_custom_params2); + ASSERT_FALSE(result4.get().Success()); +} diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp new file mode 100644 index 00000000000..a73f7b462a8 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp @@ -0,0 +1,73 @@ +//===-- GDBRemoteCommunicationServerTest.cpp --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "GDBRemoteTestUtils.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" +#include "lldb/Utility/Connection.h" + +namespace lldb_private { +namespace process_gdb_remote { + +TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_ErrorNumber) { + MockServerWithMockConnection server; + server.SendErrorResponse(0x42); + + EXPECT_THAT(server.GetPackets(), testing::ElementsAre("$E42#ab")); +} + +TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_Status) { + MockServerWithMockConnection server; + Status status; + + status.SetError(0x42, lldb::eErrorTypeGeneric); + status.SetErrorString("Test error message"); + server.SendErrorResponse(status); + + EXPECT_THAT( + server.GetPackets(), + testing::ElementsAre("$E42;54657374206572726f72206d657373616765#ad")); +} + +TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_UnimplementedError) { + MockServerWithMockConnection server; + + auto error = + llvm::make_error<PacketUnimplementedError>("Test unimplemented error"); + server.SendErrorResponse(std::move(error)); + + EXPECT_THAT(server.GetPackets(), testing::ElementsAre("$#00")); +} + +TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_StringError) { + MockServerWithMockConnection server; + + auto error = llvm::createStringError(llvm::inconvertibleErrorCode(), + "String error test"); + server.SendErrorResponse(std::move(error)); + + EXPECT_THAT( + server.GetPackets(), + testing::ElementsAre("$Eff;537472696e67206572726f722074657374#b0")); +} + +TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_ErrorList) { + MockServerWithMockConnection server; + + auto error = llvm::joinErrors(llvm::make_error<PacketUnimplementedError>(), + llvm::make_error<PacketUnimplementedError>()); + + server.SendErrorResponse(std::move(error)); + // Make sure only one packet is sent even when there are multiple errors. + EXPECT_EQ(server.GetPackets().size(), 1UL); +} + +} // namespace process_gdb_remote +} // namespace lldb_private diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp new file mode 100644 index 00000000000..2039b9e6d8d --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp @@ -0,0 +1,67 @@ +//===-- GDBRemoteCommunicationTest.cpp --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "GDBRemoteTestUtils.h" +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace lldb; +typedef GDBRemoteCommunication::PacketResult PacketResult; + +namespace { + +class TestClient : public GDBRemoteCommunication { +public: + TestClient() + : GDBRemoteCommunication("test.client", "test.client.listener") {} + + PacketResult ReadPacket(StringExtractorGDBRemote &response) { + return GDBRemoteCommunication::ReadPacket(response, std::chrono::seconds(1), + /*sync_on_timeout*/ false); + } +}; + +class GDBRemoteCommunicationTest : public GDBRemoteTest { +public: + void SetUp() override { + ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server), + llvm::Succeeded()); + } + +protected: + TestClient client; + MockServer server; + + bool Write(llvm::StringRef packet) { + ConnectionStatus status; + return server.Write(packet.data(), packet.size(), status, nullptr) == + packet.size(); + } +}; +} // end anonymous namespace + +TEST_F(GDBRemoteCommunicationTest, ReadPacket_checksum) { + struct TestCase { + llvm::StringLiteral Packet; + llvm::StringLiteral Payload; + }; + static constexpr TestCase Tests[] = { + {{"$#00"}, {""}}, + {{"$foobar#79"}, {"foobar"}}, + {{"$}}#fa"}, {"]"}}, + {{"$x*%#c7"}, {"xxxxxxxxx"}}, + }; + for (const auto &Test : Tests) { + SCOPED_TRACE(Test.Packet + " -> " + Test.Payload); + StringExtractorGDBRemote response; + ASSERT_TRUE(Write(Test.Packet)); + ASSERT_EQ(PacketResult::Success, client.ReadPacket(response)); + ASSERT_EQ(Test.Payload, response.GetStringRef()); + ASSERT_EQ(PacketResult::Success, server.GetAck()); + } +} diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp new file mode 100644 index 00000000000..92e16d981fe --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp @@ -0,0 +1,23 @@ +//===-- GDBRemoteTestUtils.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteTestUtils.h" +#include "lldb/Host/Socket.h" +#include "llvm/Testing/Support/Error.h" + +namespace lldb_private { +namespace process_gdb_remote { + +void GDBRemoteTest::SetUpTestCase() { + ASSERT_THAT_ERROR(Socket::Initialize(), llvm::Succeeded()); +} + +void GDBRemoteTest::TearDownTestCase() { Socket::Terminate(); } + +} // namespace process_gdb_remote +} // namespace lldb_private diff --git a/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.h b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.h new file mode 100644 index 00000000000..53e94a39e8b --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/gdb-remote/GDBRemoteTestUtils.h @@ -0,0 +1,91 @@ +//===-- GDBRemoteTestUtils.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h +#define lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" +#include "lldb/Utility/Connection.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); +}; + +class MockConnection : public lldb_private::Connection { +public: + MockConnection(std::vector<std::string> &packets) { m_packets = &packets; }; + + MOCK_METHOD2(Connect, + lldb::ConnectionStatus(llvm::StringRef url, Status *error_ptr)); + MOCK_METHOD5(Read, size_t(void *dst, size_t dst_len, + const Timeout<std::micro> &timeout, + lldb::ConnectionStatus &status, Status *error_ptr)); + MOCK_METHOD0(GetURI, std::string()); + MOCK_METHOD0(InterruptRead, bool()); + + lldb::ConnectionStatus Disconnect(Status *error_ptr) { + return lldb::eConnectionStatusSuccess; + }; + + bool IsConnected() const { return true; }; + size_t Write(const void *dst, size_t dst_len, lldb::ConnectionStatus &status, + Status *error_ptr) { + m_packets->emplace_back(static_cast<const char *>(dst), dst_len); + return dst_len; + }; + + lldb::IOObjectSP GetReadObject() { return lldb::IOObjectSP(); } + + std::vector<std::string> *m_packets; +}; + +class MockServer : public GDBRemoteCommunicationServer { +public: + MockServer() + : GDBRemoteCommunicationServer("mock-server", "mock-server.listener") { + m_send_acks = false; + m_send_error_strings = true; + } + + PacketResult SendPacket(llvm::StringRef payload) { + return GDBRemoteCommunicationServer::SendPacketNoLock(payload); + } + + PacketResult GetPacket(StringExtractorGDBRemote &response) { + const bool sync_on_timeout = false; + return WaitForPacketNoLock(response, std::chrono::seconds(1), + sync_on_timeout); + } + + using GDBRemoteCommunicationServer::SendErrorResponse; + using GDBRemoteCommunicationServer::SendOKResponse; + using GDBRemoteCommunicationServer::SendUnimplementedResponse; +}; + +class MockServerWithMockConnection : public MockServer { +public: + MockServerWithMockConnection() : MockServer() { + SetConnection(new MockConnection(m_packets)); + } + + llvm::ArrayRef<std::string> GetPackets() { return m_packets; }; + + std::vector<std::string> m_packets; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h diff --git a/gnu/llvm/lldb/unittests/Process/minidump/CMakeLists.txt b/gnu/llvm/lldb/unittests/Process/minidump/CMakeLists.txt new file mode 100644 index 00000000000..ad5f1883147 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/CMakeLists.txt @@ -0,0 +1,25 @@ +add_lldb_unittest(LLDBMinidumpTests + MinidumpParserTest.cpp + RegisterContextMinidumpTest.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbTarget + lldbPluginProcessUtility + lldbPluginProcessMinidump + lldbUtilityHelpers + LLVMTestingSupport + LINK_COMPONENTS + ObjectYAML + Support + ) + +set(test_inputs + fizzbuzz_no_heap.dmp + fizzbuzz_wow64.dmp + linux-x86_64.dmp + regions-memlist64.dmp + ) + +add_unittest_inputs(LLDBMinidumpTests "${test_inputs}") diff --git a/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp Binary files differnew file mode 100644 index 00000000000..19008c91fc3 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp diff --git a/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp Binary files differnew file mode 100644 index 00000000000..3d97186f2cd --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp diff --git a/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.cpp b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.cpp new file mode 100644 index 00000000000..827fe67b503 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.cpp @@ -0,0 +1,28 @@ +// Example source from breakpad's linux tutorial +// https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include "client/linux/handler/exception_handler.h" + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, + void *context, bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +void crash() { + volatile int *a = (int *)(NULL); + *a = 1; +} + +int main(int argc, char *argv[]) { + google_breakpad::MinidumpDescriptor descriptor("/tmp"); + google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, + true, -1); + printf("pid: %d\n", getpid()); + crash(); + return 0; +} diff --git a/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.dmp b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.dmp Binary files differnew file mode 100644 index 00000000000..29a12d6a2eb --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/linux-x86_64.dmp diff --git a/gnu/llvm/lldb/unittests/Process/minidump/Inputs/regions-memlist64.dmp b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/regions-memlist64.dmp Binary files differnew file mode 100644 index 00000000000..1bb8db8464d --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/Inputs/regions-memlist64.dmp diff --git a/gnu/llvm/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/gnu/llvm/lldb/unittests/Process/minidump/MinidumpParserTest.cpp new file mode 100644 index 00000000000..94bb1b395f0 --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/MinidumpParserTest.cpp @@ -0,0 +1,724 @@ +//===-- MinidumpTypesTest.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/minidump/MinidumpParser.h" +#include "Plugins/Process/minidump/MinidumpTypes.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" +#include "TestingSupport/SubsystemRAII.h" +#include "TestingSupport/TestUtilities.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/FileSpec.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +// C includes + +// C++ includes +#include <memory> + +using namespace lldb_private; +using namespace minidump; + +class MinidumpParserTest : public testing::Test { +public: + SubsystemRAII<FileSystem> subsystems; + + void SetUpData(const char *minidump_filename) { + std::string filename = GetInputFilePath(minidump_filename); + auto BufferPtr = FileSystem::Instance().CreateDataBuffer(filename, -1, 0); + ASSERT_NE(BufferPtr, nullptr); + llvm::Expected<MinidumpParser> expected_parser = + MinidumpParser::Create(BufferPtr); + ASSERT_THAT_EXPECTED(expected_parser, llvm::Succeeded()); + parser = std::move(*expected_parser); + ASSERT_GT(parser->GetData().size(), 0UL); + } + + llvm::Error SetUpFromYaml(llvm::StringRef yaml) { + std::string data; + llvm::raw_string_ostream os(data); + llvm::yaml::Input YIn(yaml); + if (!llvm::yaml::convertYAML(YIn, os, [](const llvm::Twine &Msg) {})) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "convertYAML() failed"); + + os.flush(); + auto data_buffer_sp = + std::make_shared<DataBufferHeap>(data.data(), data.size()); + auto expected_parser = MinidumpParser::Create(std::move(data_buffer_sp)); + if (!expected_parser) + return expected_parser.takeError(); + parser = std::move(*expected_parser); + return llvm::Error::success(); + } + + llvm::Optional<MinidumpParser> parser; +}; + +TEST_F(MinidumpParserTest, InvalidMinidump) { + std::string duplicate_streams; + llvm::raw_string_ostream os(duplicate_streams); + llvm::yaml::Input YIn(R"( +--- !minidump +Streams: + - Type: LinuxAuxv + Content: DEADBEEFBAADF00D + - Type: LinuxAuxv + Content: DEADBEEFBAADF00D + )"); + + ASSERT_TRUE(llvm::yaml::convertYAML(YIn, os, [](const llvm::Twine &Msg){})); + os.flush(); + auto data_buffer_sp = std::make_shared<DataBufferHeap>( + duplicate_streams.data(), duplicate_streams.size()); + ASSERT_THAT_EXPECTED(MinidumpParser::Create(data_buffer_sp), llvm::Failed()); +} + +TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ThreadList + Threads: + - Thread Id: 0x00003E81 + Stack: + Start of Memory Range: 0x00007FFCEB34A000 + Content: C84D04BCE97F00 + Context: 00000000000000 +... +)"), + llvm::Succeeded()); + llvm::ArrayRef<minidump::Thread> thread_list; + + thread_list = parser->GetThreads(); + ASSERT_EQ(1UL, thread_list.size()); + + const minidump::Thread &thread = thread_list[0]; + + EXPECT_EQ(0x3e81u, thread.ThreadId); + + llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread); + EXPECT_EQ(7u, context.size()); +} + +TEST_F(MinidumpParserTest, GetArchitecture) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Processor Level: 6 + Processor Revision: 16130 + Number of Processors: 1 + Platform ID: Linux + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 +... +)"), + llvm::Succeeded()); + ASSERT_EQ(llvm::Triple::ArchType::x86_64, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Linux, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetMiscInfo_no_stream) { + // Test that GetMiscInfo returns nullptr when the minidump does not contain + // this stream. + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: +... +)"), + llvm::Succeeded()); + EXPECT_EQ(nullptr, parser->GetMiscInfo()); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatus) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Processor Level: 6 + Processor Revision: 16130 + Number of Processors: 1 + Platform ID: Linux + CSD Version: 'Linux 3.13.0-91-generic' + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 + - Type: LinuxProcStatus + Text: | + Name: a.out + State: t (tracing stop) + Tgid: 16001 + Ngid: 0 + Pid: 16001 + PPid: 13243 + TracerPid: 16002 + Uid: 404696 404696 404696 404696 + Gid: 5762 5762 5762 5762 +... +)"), + llvm::Succeeded()); + llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus(); + ASSERT_TRUE(proc_status.hasValue()); + lldb::pid_t pid = proc_status->GetPid(); + ASSERT_EQ(16001UL, pid); +} + +TEST_F(MinidumpParserTest, GetPid) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Processor Level: 6 + Processor Revision: 16130 + Number of Processors: 1 + Platform ID: Linux + CSD Version: 'Linux 3.13.0-91-generic' + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 + - Type: LinuxProcStatus + Text: | + Name: a.out + State: t (tracing stop) + Tgid: 16001 + Ngid: 0 + Pid: 16001 + PPid: 13243 + TracerPid: 16002 + Uid: 404696 404696 404696 404696 + Gid: 5762 5762 5762 5762 +... +)"), + llvm::Succeeded()); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(16001UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetFilteredModuleList) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ModuleList + Modules: + - Base of Image: 0x0000000000400000 + Size of Image: 0x00001000 + Module Name: '/tmp/test/linux-x86_64_not_crashed' + CodeView Record: 4C4570426CCF3F60FFA7CC4B86AE8FF44DB2576A68983611 + - Base of Image: 0x0000000000600000 + Size of Image: 0x00002000 + Module Name: '/tmp/test/linux-x86_64_not_crashed' + CodeView Record: 4C4570426CCF3F60FFA7CC4B86AE8FF44DB2576A68983611 +... +)"), + llvm::Succeeded()); + llvm::ArrayRef<minidump::Module> modules = parser->GetModuleList(); + std::vector<const minidump::Module *> filtered_modules = + parser->GetFilteredModuleList(); + EXPECT_EQ(2u, modules.size()); + ASSERT_EQ(1u, filtered_modules.size()); + const minidump::Module &M = *filtered_modules[0]; + EXPECT_THAT_EXPECTED(parser->GetMinidumpFile().getString(M.ModuleNameRVA), + llvm::HasValue("/tmp/test/linux-x86_64_not_crashed")); +} + +TEST_F(MinidumpParserTest, GetExceptionStream) { + SetUpData("linux-x86_64.dmp"); + const llvm::minidump::ExceptionStream *exception_stream = + parser->GetExceptionStream(); + ASSERT_NE(nullptr, exception_stream); + ASSERT_EQ(11UL, exception_stream->ExceptionRecord.ExceptionCode); +} + +void check_mem_range_exists(MinidumpParser &parser, const uint64_t range_start, + const uint64_t range_size) { + llvm::Optional<minidump::Range> range = parser.FindMemoryRange(range_start); + ASSERT_TRUE(range.hasValue()) << "There is no range containing this address"; + EXPECT_EQ(range_start, range->start); + EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size()); +} + +TEST_F(MinidumpParserTest, FindMemoryRange) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: MemoryList + Memory Ranges: + - Start of Memory Range: 0x00007FFCEB34A000 + Content: C84D04BCE9 + - Start of Memory Range: 0x0000000000401D46 + Content: 5421 +... +)"), + llvm::Succeeded()); + EXPECT_EQ(llvm::None, parser->FindMemoryRange(0x00)); + EXPECT_EQ(llvm::None, parser->FindMemoryRange(0x2a)); + EXPECT_EQ((minidump::Range{0x401d46, llvm::ArrayRef<uint8_t>{0x54, 0x21}}), + parser->FindMemoryRange(0x401d46)); + EXPECT_EQ(llvm::None, parser->FindMemoryRange(0x401d46 + 2)); + + EXPECT_EQ( + (minidump::Range{0x7ffceb34a000, + llvm::ArrayRef<uint8_t>{0xc8, 0x4d, 0x04, 0xbc, 0xe9}}), + parser->FindMemoryRange(0x7ffceb34a000 + 2)); + EXPECT_EQ(llvm::None, parser->FindMemoryRange(0x7ffceb34a000 + 5)); +} + +TEST_F(MinidumpParserTest, GetMemory) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: MemoryList + Memory Ranges: + - Start of Memory Range: 0x00007FFCEB34A000 + Content: C84D04BCE9 + - Start of Memory Range: 0x0000000000401D46 + Content: 5421 +... +)"), + llvm::Succeeded()); + + EXPECT_EQ((llvm::ArrayRef<uint8_t>{0x54}), parser->GetMemory(0x401d46, 1)); + EXPECT_EQ((llvm::ArrayRef<uint8_t>{0x54, 0x21}), + parser->GetMemory(0x401d46, 4)); + + EXPECT_EQ((llvm::ArrayRef<uint8_t>{0xc8, 0x4d, 0x04, 0xbc, 0xe9}), + parser->GetMemory(0x7ffceb34a000, 5)); + EXPECT_EQ((llvm::ArrayRef<uint8_t>{0xc8, 0x4d, 0x04}), + parser->GetMemory(0x7ffceb34a000, 3)); + + EXPECT_EQ(llvm::ArrayRef<uint8_t>(), parser->GetMemory(0x500000, 512)); +} + +TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) { + SetUpData("fizzbuzz_wow64.dmp"); + + // There are a lot of ranges in the file, just testing with some of them + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + check_mem_range_exists(*parser, 0x10000, 65536); // first range + check_mem_range_exists(*parser, 0x40000, 4096); + EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue()); + check_mem_range_exists(*parser, 0x77c12000, 8192); + check_mem_range_exists(*parser, 0x7ffe0000, 4096); // last range + EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue()); +} + +constexpr auto yes = MemoryRegionInfo::eYes; +constexpr auto no = MemoryRegionInfo::eNo; +constexpr auto unknown = MemoryRegionInfo::eDontKnow; + +TEST_F(MinidumpParserTest, GetMemoryRegionInfo) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: MemoryInfoList + Memory Ranges: + - Base Address: 0x0000000000000000 + Allocation Protect: [ ] + Region Size: 0x0000000000010000 + State: [ MEM_FREE ] + Protect: [ PAGE_NO_ACCESS ] + Type: [ ] + - Base Address: 0x0000000000010000 + Allocation Protect: [ PAGE_READ_WRITE ] + Region Size: 0x0000000000021000 + State: [ MEM_COMMIT ] + Type: [ MEM_MAPPED ] + - Base Address: 0x0000000000040000 + Allocation Protect: [ PAGE_EXECUTE_WRITE_COPY ] + Region Size: 0x0000000000001000 + State: [ MEM_COMMIT ] + Protect: [ PAGE_READ_ONLY ] + Type: [ MEM_IMAGE ] + - Base Address: 0x000000007FFE0000 + Allocation Protect: [ PAGE_READ_ONLY ] + Region Size: 0x0000000000001000 + State: [ MEM_COMMIT ] + Type: [ MEM_PRIVATE ] + - Base Address: 0x000000007FFE1000 + Allocation Base: 0x000000007FFE0000 + Allocation Protect: [ PAGE_READ_ONLY ] + Region Size: 0x000000000000F000 + State: [ MEM_RESERVE ] + Protect: [ PAGE_NO_ACCESS ] + Type: [ MEM_PRIVATE ] +... +)"), + llvm::Succeeded()); + + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x0, 0x10000}, no, no, no, no, + ConstString(), unknown, 0), + MemoryRegionInfo({0x10000, 0x21000}, yes, yes, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x40000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x7ffe0000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x7ffe1000, 0xf000}, no, no, no, yes, + ConstString(), unknown, 0)), + true)); +} + +TEST_F(MinidumpParserTest, GetMemoryRegionInfoFromMemoryList) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: MemoryList + Memory Ranges: + - Start of Memory Range: 0x0000000000001000 + Content: '31313131313131313131313131313131' + - Start of Memory Range: 0x0000000000002000 + Content: '3333333333333333333333333333333333333333333333333333333333333333' +... +)"), + llvm::Succeeded()); + + // Test we can get memory regions from the MINIDUMP_MEMORY_LIST stream when + // we don't have a MemoryInfoListStream. + + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x1000, 0x10}, yes, unknown, unknown, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x2000, 0x20}, yes, unknown, unknown, + yes, ConstString(), unknown, 0)), + false)); +} + +TEST_F(MinidumpParserTest, GetMemoryRegionInfoFromMemory64List) { + SetUpData("regions-memlist64.dmp"); + + // Test we can get memory regions from the MINIDUMP_MEMORY64_LIST stream when + // we don't have a MemoryInfoListStream. + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x1000, 0x10}, yes, unknown, unknown, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x2000, 0x20}, yes, unknown, unknown, + yes, ConstString(), unknown, 0)), + false)); +} + +TEST_F(MinidumpParserTest, GetMemoryRegionInfoLinuxMaps) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: LinuxMaps + Text: | + 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process + 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process + 400dc000-400dd000 rw-p 00000000 00:00 0 + 400ec000-400ed000 r--p 00000000 00:00 0 + 400ee000-400ef000 rw-p 00010000 b3:04 300 /system/bin/linker + 400fc000-400fd000 rwxp 00001000 b3:04 1096 /system/lib/liblog.so + +... +)"), + llvm::Succeeded()); + // Test we can get memory regions from the linux /proc/<pid>/maps stream when + // we don't have a MemoryInfoListStream. + ConstString app_process("/system/bin/app_process"); + ConstString linker("/system/bin/linker"); + ConstString liblog("/system/lib/liblog.so"); + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x400d9000, 0x2000}, yes, no, yes, + yes, app_process, unknown, 0), + MemoryRegionInfo({0x400db000, 0x1000}, yes, no, no, yes, + app_process, unknown, 0), + MemoryRegionInfo({0x400dc000, 0x1000}, yes, yes, no, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x400ec000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x400ee000, 0x1000}, yes, yes, no, + yes, linker, unknown, 0), + MemoryRegionInfo({0x400fc000, 0x1000}, yes, yes, yes, + yes, liblog, unknown, 0)), + true)); +} + +// Windows Minidump tests +TEST_F(MinidumpParserTest, GetArchitectureWindows) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: X86 + Processor Level: 6 + Processor Revision: 15876 + Number of Processors: 32 + Product type: 1 + Major Version: 6 + Minor Version: 1 + Build Number: 7601 + Platform ID: Win32NT + CSD Version: Service Pack 1 + Suite Mask: 0x0100 + CPU: + Vendor ID: GenuineIntel + Version Info: 0x000306E4 + Feature Info: 0xBFEBFBFF + AMD Extended Features: 0x771EEC80 +... +)"), + llvm::Succeeded()); + ASSERT_EQ(llvm::Triple::ArchType::x86, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Win32, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatus_no_stream) { + // Test that GetLinuxProcStatus returns nullptr when the minidump does not + // contain this stream. + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: +... +)"), + llvm::Succeeded()); + EXPECT_EQ(llvm::None, parser->GetLinuxProcStatus()); +} + +TEST_F(MinidumpParserTest, GetMiscInfoWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + const MinidumpMiscInfo *misc_info = parser->GetMiscInfo(); + ASSERT_NE(nullptr, misc_info); + llvm::Optional<lldb::pid_t> pid = misc_info->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetPidWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +// wow64 +TEST_F(MinidumpParserTest, GetPidWow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(7836UL, pid.getValue()); +} + +// Register tests +#define REG_VAL32(x) *(reinterpret_cast<uint32_t *>(x)) +#define REG_VAL64(x) *(reinterpret_cast<uint64_t *>(x)) + +TEST_F(MinidumpParserTest, GetThreadContext_x86_32) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ThreadList + Threads: + - Thread Id: 0x00026804 + Stack: + Start of Memory Range: 0x00000000FF9DD000 + Content: 68D39DFF + Context: 0F0001000000000000000000000000000000000000000000000000007F03FFFF0000FFFFFFFFFFFF09DC62F72300000088E36CF72B00FFFF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063000000000000002B0000002B000000A88204085CD59DFF008077F7A3D49DFF01000000000000003CD59DFFA082040823000000820201002CD59DFF2B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +)"), + llvm::Succeeded()); + + llvm::ArrayRef<minidump::Thread> thread_list = parser->GetThreads(); + const minidump::Thread &thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + const MinidumpContext_x86_32 *context; + EXPECT_TRUE(consumeObject(registers, context).Success()); + + EXPECT_EQ(MinidumpContext_x86_32_Flags(uint32_t(context->context_flags)), + MinidumpContext_x86_32_Flags::x86_32_Flag | + MinidumpContext_x86_32_Flags::Full | + MinidumpContext_x86_32_Flags::FloatingPoint); + + EXPECT_EQ(0x00000000u, context->eax); + EXPECT_EQ(0xf7778000u, context->ebx); + EXPECT_EQ(0x00000001u, context->ecx); + EXPECT_EQ(0xff9dd4a3u, context->edx); + EXPECT_EQ(0x080482a8u, context->edi); + EXPECT_EQ(0xff9dd55cu, context->esi); + EXPECT_EQ(0xff9dd53cu, context->ebp); + EXPECT_EQ(0xff9dd52cu, context->esp); + EXPECT_EQ(0x080482a0u, context->eip); + EXPECT_EQ(0x00010282u, context->eflags); + EXPECT_EQ(0x0023u, context->cs); + EXPECT_EQ(0x0000u, context->fs); + EXPECT_EQ(0x0063u, context->gs); + EXPECT_EQ(0x002bu, context->ss); + EXPECT_EQ(0x002bu, context->ds); + EXPECT_EQ(0x002bu, context->es); +} + +TEST_F(MinidumpParserTest, GetThreadContext_x86_64) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ThreadList + Threads: + - Thread Id: 0x00003E81 + Stack: + Start of Memory Range: 0x00007FFCEB34A000 + Content: C84D04BCE97F00 + Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +... +)"), + llvm::Succeeded()); + llvm::ArrayRef<minidump::Thread> thread_list = parser->GetThreads(); + const minidump::Thread &thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + const MinidumpContext_x86_64 *context; + EXPECT_TRUE(consumeObject(registers, context).Success()); + + EXPECT_EQ(MinidumpContext_x86_64_Flags(uint32_t(context->context_flags)), + MinidumpContext_x86_64_Flags::x86_64_Flag | + MinidumpContext_x86_64_Flags::Control | + MinidumpContext_x86_64_Flags::FloatingPoint | + MinidumpContext_x86_64_Flags::Integer); + EXPECT_EQ(0x0000000000000000u, context->rax); + EXPECT_EQ(0x0000000000000000u, context->rbx); + EXPECT_EQ(0x0000000000000010u, context->rcx); + EXPECT_EQ(0x0000000000000000u, context->rdx); + EXPECT_EQ(0x00007ffceb349cf0u, context->rdi); + EXPECT_EQ(0x0000000000000000u, context->rsi); + EXPECT_EQ(0x00007ffceb34a210u, context->rbp); + EXPECT_EQ(0x00007ffceb34a210u, context->rsp); + EXPECT_EQ(0x00007fe9bc1aa9c0u, context->r8); + EXPECT_EQ(0x0000000000000000u, context->r9); + EXPECT_EQ(0x00007fe9bc3f16a0u, context->r10); + EXPECT_EQ(0x0000000000000246u, context->r11); + EXPECT_EQ(0x0000000000401c92u, context->r12); + EXPECT_EQ(0x00007ffceb34a430u, context->r13); + EXPECT_EQ(0x0000000000000000u, context->r14); + EXPECT_EQ(0x0000000000000000u, context->r15); + EXPECT_EQ(0x0000000000401dc6u, context->rip); + EXPECT_EQ(0x00010206u, context->eflags); + EXPECT_EQ(0x0033u, context->cs); + EXPECT_EQ(0x0000u, context->ss); +} + +TEST_F(MinidumpParserTest, GetThreadContext_x86_32_wow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::ArrayRef<minidump::Thread> thread_list = parser->GetThreads(); + const minidump::Thread &thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContextWow64(thread)); + const MinidumpContext_x86_32 *context; + EXPECT_TRUE(consumeObject(registers, context).Success()); + + EXPECT_EQ(MinidumpContext_x86_32_Flags(uint32_t(context->context_flags)), + MinidumpContext_x86_32_Flags::x86_32_Flag | + MinidumpContext_x86_32_Flags::Full | + MinidumpContext_x86_32_Flags::FloatingPoint | + MinidumpContext_x86_32_Flags::ExtendedRegisters); + + EXPECT_EQ(0x00000000u, context->eax); + EXPECT_EQ(0x0037f608u, context->ebx); + EXPECT_EQ(0x00e61578u, context->ecx); + EXPECT_EQ(0x00000008u, context->edx); + EXPECT_EQ(0x00000000u, context->edi); + EXPECT_EQ(0x00000002u, context->esi); + EXPECT_EQ(0x0037f654u, context->ebp); + EXPECT_EQ(0x0037f5b8u, context->esp); + EXPECT_EQ(0x77ce01fdu, context->eip); + EXPECT_EQ(0x00000246u, context->eflags); + EXPECT_EQ(0x0023u, context->cs); + EXPECT_EQ(0x0053u, context->fs); + EXPECT_EQ(0x002bu, context->gs); + EXPECT_EQ(0x002bu, context->ss); + EXPECT_EQ(0x002bu, context->ds); + EXPECT_EQ(0x002bu, context->es); +} + +TEST_F(MinidumpParserTest, MinidumpDuplicateModuleMinAddress) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ModuleList + Modules: + - Base of Image: 0x0000000000002000 + Size of Image: 0x00001000 + Module Name: '/tmp/a' + CodeView Record: '' + - Base of Image: 0x0000000000001000 + Size of Image: 0x00001000 + Module Name: '/tmp/a' + CodeView Record: '' +... +)"), + llvm::Succeeded()); + // If we have a module mentioned twice in the module list, the filtered + // module list should contain the instance with the lowest BaseOfImage. + std::vector<const minidump::Module *> filtered_modules = + parser->GetFilteredModuleList(); + ASSERT_EQ(1u, filtered_modules.size()); + EXPECT_EQ(0x0000000000001000u, filtered_modules[0]->BaseOfImage); +} + +TEST_F(MinidumpParserTest, MinidumpModuleOrder) { + ASSERT_THAT_ERROR(SetUpFromYaml(R"( +--- !minidump +Streams: + - Type: ModuleList + Modules: + - Base of Image: 0x0000000000002000 + Size of Image: 0x00001000 + Module Name: '/tmp/a' + CodeView Record: '' + - Base of Image: 0x0000000000001000 + Size of Image: 0x00001000 + Module Name: '/tmp/b' + CodeView Record: '' +... +)"), + llvm::Succeeded()); + // Test module filtering does not affect the overall module order. Previous + // versions of the MinidumpParser::GetFilteredModuleList() function would sort + // all images by address and modify the order of the modules. + std::vector<const minidump::Module *> filtered_modules = + parser->GetFilteredModuleList(); + ASSERT_EQ(2u, filtered_modules.size()); + EXPECT_EQ(0x0000000000002000u, filtered_modules[0]->BaseOfImage); + EXPECT_THAT_EXPECTED( + parser->GetMinidumpFile().getString(filtered_modules[0]->ModuleNameRVA), + llvm::HasValue("/tmp/a")); + EXPECT_EQ(0x0000000000001000u, filtered_modules[1]->BaseOfImage); + EXPECT_THAT_EXPECTED( + parser->GetMinidumpFile().getString(filtered_modules[1]->ModuleNameRVA), + llvm::HasValue("/tmp/b")); +} + diff --git a/gnu/llvm/lldb/unittests/Process/minidump/RegisterContextMinidumpTest.cpp b/gnu/llvm/lldb/unittests/Process/minidump/RegisterContextMinidumpTest.cpp new file mode 100644 index 00000000000..265da63d33a --- /dev/null +++ b/gnu/llvm/lldb/unittests/Process/minidump/RegisterContextMinidumpTest.cpp @@ -0,0 +1,201 @@ +//===-- RegisterContextMinidumpTest.cpp -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_ARM.h" +#include "lldb/Utility/DataBuffer.h" +#include "llvm/ADT/StringRef.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace lldb_private::minidump; + +static uint32_t reg32(const DataBuffer &Buf, const RegisterInfo &Info) { + return *reinterpret_cast<const uint32_t *>(Buf.GetBytes() + Info.byte_offset); +} + +static uint64_t reg64(const DataBuffer &Buf, const RegisterInfo &Info) { + return *reinterpret_cast<const uint64_t *>(Buf.GetBytes() + Info.byte_offset); +} + +TEST(RegisterContextMinidump, ConvertMinidumpContext_x86_32) { + MinidumpContext_x86_32 Context; + Context.context_flags = + static_cast<uint32_t>(MinidumpContext_x86_32_Flags::x86_32_Flag | + MinidumpContext_x86_32_Flags::Control | + MinidumpContext_x86_32_Flags::Segments | + MinidumpContext_x86_32_Flags::Integer); + Context.eax = 0x00010203; + Context.ebx = 0x04050607; + Context.ecx = 0x08090a0b; + Context.edx = 0x0c0d0e0f; + Context.edi = 0x10111213; + Context.esi = 0x14151617; + Context.ebp = 0x18191a1b; + Context.esp = 0x1c1d1e1f; + Context.eip = 0x20212223; + Context.eflags = 0x24252627; + Context.cs = 0x2829; + Context.fs = 0x2a2b; + Context.gs = 0x2c2d; + Context.ss = 0x2e2f; + Context.ds = 0x3031; + Context.es = 0x3233; + llvm::ArrayRef<uint8_t> ContextRef(reinterpret_cast<uint8_t *>(&Context), + sizeof(Context)); + + ArchSpec arch("i386-pc-linux"); + auto RegInterface = std::make_unique<RegisterContextLinux_i386>(arch); + lldb::DataBufferSP Buf = + ConvertMinidumpContext_x86_32(ContextRef, RegInterface.get()); + ASSERT_EQ(RegInterface->GetGPRSize(), Buf->GetByteSize()); + + const RegisterInfo *Info = RegInterface->GetRegisterInfo(); + ASSERT_NE(nullptr, Info); + + EXPECT_EQ(Context.eax, reg32(*Buf, Info[lldb_eax_i386])); + EXPECT_EQ(Context.ebx, reg32(*Buf, Info[lldb_ebx_i386])); + EXPECT_EQ(Context.ecx, reg32(*Buf, Info[lldb_ecx_i386])); + EXPECT_EQ(Context.edx, reg32(*Buf, Info[lldb_edx_i386])); + EXPECT_EQ(Context.edi, reg32(*Buf, Info[lldb_edi_i386])); + EXPECT_EQ(Context.esi, reg32(*Buf, Info[lldb_esi_i386])); + EXPECT_EQ(Context.ebp, reg32(*Buf, Info[lldb_ebp_i386])); + EXPECT_EQ(Context.esp, reg32(*Buf, Info[lldb_esp_i386])); + EXPECT_EQ(Context.eip, reg32(*Buf, Info[lldb_eip_i386])); + EXPECT_EQ(Context.eflags, reg32(*Buf, Info[lldb_eflags_i386])); + EXPECT_EQ(Context.cs, reg32(*Buf, Info[lldb_cs_i386])); + EXPECT_EQ(Context.fs, reg32(*Buf, Info[lldb_fs_i386])); + EXPECT_EQ(Context.gs, reg32(*Buf, Info[lldb_gs_i386])); + EXPECT_EQ(Context.ss, reg32(*Buf, Info[lldb_ss_i386])); + EXPECT_EQ(Context.ds, reg32(*Buf, Info[lldb_ds_i386])); + EXPECT_EQ(Context.es, reg32(*Buf, Info[lldb_es_i386])); +} + +TEST(RegisterContextMinidump, ConvertMinidumpContext_x86_64) { + MinidumpContext_x86_64 Context; + Context.context_flags = + static_cast<uint32_t>(MinidumpContext_x86_64_Flags::x86_64_Flag | + MinidumpContext_x86_64_Flags::Control | + MinidumpContext_x86_64_Flags::Segments | + MinidumpContext_x86_64_Flags::Integer); + Context.rax = 0x0001020304050607; + Context.rbx = 0x08090a0b0c0d0e0f; + Context.rcx = 0x1011121314151617; + Context.rdx = 0x18191a1b1c1d1e1f; + Context.rdi = 0x2021222324252627; + Context.rsi = 0x28292a2b2c2d2e2f; + Context.rbp = 0x3031323334353637; + Context.rsp = 0x38393a3b3c3d3e3f; + Context.r8 = 0x4041424344454647; + Context.r9 = 0x48494a4b4c4d4e4f; + Context.r10 = 0x5051525354555657; + Context.r11 = 0x58595a5b5c5d5e5f; + Context.r12 = 0x6061626364656667; + Context.r13 = 0x68696a6b6c6d6e6f; + Context.r14 = 0x7071727374757677; + Context.r15 = 0x78797a7b7c7d7e7f; + Context.rip = 0x8081828384858687; + Context.eflags = 0x88898a8b; + Context.cs = 0x8c8d; + Context.fs = 0x8e8f; + Context.gs = 0x9091; + Context.ss = 0x9293; + Context.ds = 0x9495; + Context.ss = 0x9697; + llvm::ArrayRef<uint8_t> ContextRef(reinterpret_cast<uint8_t *>(&Context), + sizeof(Context)); + + ArchSpec arch("x86_64-pc-linux"); + auto RegInterface = std::make_unique<RegisterContextLinux_x86_64>(arch); + lldb::DataBufferSP Buf = + ConvertMinidumpContext_x86_64(ContextRef, RegInterface.get()); + ASSERT_EQ(RegInterface->GetGPRSize(), Buf->GetByteSize()); + + const RegisterInfo *Info = RegInterface->GetRegisterInfo(); + EXPECT_EQ(Context.rax, reg64(*Buf, Info[lldb_rax_x86_64])); + EXPECT_EQ(Context.rbx, reg64(*Buf, Info[lldb_rbx_x86_64])); + EXPECT_EQ(Context.rcx, reg64(*Buf, Info[lldb_rcx_x86_64])); + EXPECT_EQ(Context.rdx, reg64(*Buf, Info[lldb_rdx_x86_64])); + EXPECT_EQ(Context.rdi, reg64(*Buf, Info[lldb_rdi_x86_64])); + EXPECT_EQ(Context.rsi, reg64(*Buf, Info[lldb_rsi_x86_64])); + EXPECT_EQ(Context.rbp, reg64(*Buf, Info[lldb_rbp_x86_64])); + EXPECT_EQ(Context.rsp, reg64(*Buf, Info[lldb_rsp_x86_64])); + EXPECT_EQ(Context.r8, reg64(*Buf, Info[lldb_r8_x86_64])); + EXPECT_EQ(Context.r9, reg64(*Buf, Info[lldb_r9_x86_64])); + EXPECT_EQ(Context.r10, reg64(*Buf, Info[lldb_r10_x86_64])); + EXPECT_EQ(Context.r11, reg64(*Buf, Info[lldb_r11_x86_64])); + EXPECT_EQ(Context.r12, reg64(*Buf, Info[lldb_r12_x86_64])); + EXPECT_EQ(Context.r13, reg64(*Buf, Info[lldb_r13_x86_64])); + EXPECT_EQ(Context.r14, reg64(*Buf, Info[lldb_r14_x86_64])); + EXPECT_EQ(Context.r15, reg64(*Buf, Info[lldb_r15_x86_64])); + EXPECT_EQ(Context.rip, reg64(*Buf, Info[lldb_rip_x86_64])); + EXPECT_EQ(Context.eflags, reg64(*Buf, Info[lldb_rflags_x86_64])); + EXPECT_EQ(Context.cs, reg64(*Buf, Info[lldb_cs_x86_64])); + EXPECT_EQ(Context.fs, reg64(*Buf, Info[lldb_fs_x86_64])); + EXPECT_EQ(Context.gs, reg64(*Buf, Info[lldb_gs_x86_64])); + EXPECT_EQ(Context.ss, reg64(*Buf, Info[lldb_ss_x86_64])); + EXPECT_EQ(Context.ds, reg64(*Buf, Info[lldb_ds_x86_64])); + EXPECT_EQ(Context.es, reg64(*Buf, Info[lldb_es_x86_64])); +} + +static void TestARMRegInfo(const lldb_private::RegisterInfo *info) { + // Make sure we have valid register numbers for eRegisterKindEHFrame and + // eRegisterKindDWARF for GPR registers r0-r15 so that we can unwind + // correctly when using this information. + llvm::StringRef name(info->name); + llvm::StringRef alt_name(info->alt_name); + if (name.startswith("r") || alt_name.startswith("r")) { + EXPECT_NE(info->kinds[lldb::eRegisterKindEHFrame], LLDB_INVALID_REGNUM); + EXPECT_NE(info->kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM); + } + // Verify generic register are set correctly + if (name == "r0") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_ARG1); + } else if (name == "r1") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_ARG2); + } else if (name == "r2") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_ARG3); + } else if (name == "r3") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_ARG4); + } else if (name == "sp") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_SP); + } else if (name == "fp") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_FP); + } else if (name == "lr") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_RA); + } else if (name == "pc") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_PC); + } else if (name == "cpsr") { + EXPECT_EQ(info->kinds[lldb::eRegisterKindGeneric], + (uint32_t)LLDB_REGNUM_GENERIC_FLAGS); + } +} + +TEST(RegisterContextMinidump, CheckRegisterContextMinidump_ARM) { + size_t num_regs = RegisterContextMinidump_ARM::GetRegisterCountStatic(); + const lldb_private::RegisterInfo *reg_info; + for (size_t reg=0; reg<num_regs; ++reg) { + reg_info = RegisterContextMinidump_ARM::GetRegisterInfoAtIndexStatic(reg, + true); + TestARMRegInfo(reg_info); + reg_info = RegisterContextMinidump_ARM::GetRegisterInfoAtIndexStatic(reg, + false); + TestARMRegInfo(reg_info); + } +} |