aboutsummaryrefslogtreecommitdiff
path: root/unittests/Process
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Process')
-rw-r--r--unittests/Process/CMakeLists.txt2
-rw-r--r--unittests/Process/gdb-remote/CMakeLists.txt5
-rw-r--r--unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp378
-rw-r--r--unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp307
-rw-r--r--unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp69
-rw-r--r--unittests/Process/gdb-remote/GDBRemoteTestUtils.h51
-rw-r--r--unittests/Process/minidump/CMakeLists.txt12
-rw-r--r--unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmpbin0 -> 6297 bytes
-rw-r--r--unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmpbin0 -> 9280561 bytes
-rw-r--r--unittests/Process/minidump/Inputs/linux-i386.dmpbin0 -> 32976 bytes
-rw-r--r--unittests/Process/minidump/Inputs/linux-x86_64.cpp28
-rw-r--r--unittests/Process/minidump/Inputs/linux-x86_64.dmpbin0 -> 38320 bytes
-rw-r--r--unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmpbin0 -> 63744 bytes
-rw-r--r--unittests/Process/minidump/MinidumpParserTest.cpp453
14 files changed, 1305 insertions, 0 deletions
diff --git a/unittests/Process/CMakeLists.txt b/unittests/Process/CMakeLists.txt
new file mode 100644
index 000000000000..70f59382afa1
--- /dev/null
+++ b/unittests/Process/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(gdb-remote)
+add_subdirectory(minidump)
diff --git a/unittests/Process/gdb-remote/CMakeLists.txt b/unittests/Process/gdb-remote/CMakeLists.txt
new file mode 100644
index 000000000000..de4cac11b233
--- /dev/null
+++ b/unittests/Process/gdb-remote/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_lldb_unittest(ProcessGdbRemoteTests
+ GDBRemoteClientBaseTest.cpp
+ GDBRemoteCommunicationClientTest.cpp
+ GDBRemoteTestUtils.cpp
+ )
diff --git a/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
new file mode 100644
index 000000000000..2cfd52f5767a
--- /dev/null
+++ b/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
@@ -0,0 +1,378 @@
+//===-- GDBRemoteClientBaseTest.cpp -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
+// Workaround for MSVC standard library bug, which fails to include <thread>
+// when
+// exceptions are disabled.
+#include <eh.h>
+#endif
+#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/Core/StreamGDBRemote.h"
+
+#include "llvm/ADT/STLExtras.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) { output += out; }
+ void HandleAsyncMisc(llvm::StringRef data) { misc_data += data; }
+ void HandleStopReply() { ++stop_reply_called; }
+
+ void HandleAsyncStructuredDataPacket(llvm::StringRef data) {
+ structured_data_packets.push_back(data);
+ }
+};
+
+struct TestClient : public GDBRemoteClientBase {
+ TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") {
+ m_send_acks = false;
+ }
+};
+
+struct ContinueFixture {
+ MockDelegate delegate;
+ TestClient client;
+ MockServer server;
+ ListenerSP listener_sp;
+
+ ContinueFixture();
+
+ 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);
+ }
+};
+
+ContinueFixture::ContinueFixture()
+ : listener_sp(Listener::MakeListener("listener")) {
+ Connect(client, server);
+ listener_sp->StartListeningForEvents(&client,
+ TestClient::eBroadcastBitRunPacketSent);
+}
+
+} // end anonymous namespace
+
+class GDBRemoteClientBaseTest : public GDBRemoteTest {};
+
+TEST_F(GDBRemoteClientBaseTest, SendContinueAndWait) {
+ StringExtractorGDBRemote response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Continue. The inferior will stop with a signal.
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
+ ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
+ ASSERT_EQ("T01", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+
+ // Continue. The inferior will exit.
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("W01"));
+ ASSERT_EQ(eStateExited, fix.SendCPacket(response));
+ ASSERT_EQ("W01", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+
+ // Continue. The inferior will get killed.
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("X01"));
+ ASSERT_EQ(eStateExited, fix.SendCPacket(response));
+ ASSERT_EQ("X01", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+}
+
+TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) {
+ StringExtractorGDBRemote continue_response, response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // SendAsyncSignal should do nothing when we are not running.
+ ASSERT_FALSE(fix.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 fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ std::future<bool> async_result = std::async(
+ std::launch::async, [&] { return fix.client.SendAsyncSignal(0x47); });
+
+ // First we'll get interrupted.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("\x03", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
+
+ // Then we get the signal packet.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("C47", response.GetStringRef());
+ ASSERT_TRUE(async_result.get());
+
+ // And we report back a signal stop.
+ ASSERT_EQ(PacketResult::Success, fix.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;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Continue. After the run packet is sent, send an async packet.
+ std::future<StateType> continue_state = std::async(
+ std::launch::async, [&] { return fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ // Sending without async enabled should fail.
+ ASSERT_EQ(
+ PacketResult::ErrorSendFailed,
+ fix.client.SendPacketAndWaitForResponse("qTest1", response, !send_async));
+
+ std::future<PacketResult> async_result = std::async(std::launch::async, [&] {
+ return fix.client.SendPacketAndWaitForResponse("qTest2", async_response,
+ send_async);
+ });
+
+ // First we'll get interrupted.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("\x03", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
+
+ // Then we get the async packet.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("qTest2", response.GetStringRef());
+
+ // Send the response and receive it.
+ ASSERT_EQ(PacketResult::Success, fix.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, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
+ ASSERT_EQ(eStateStopped, continue_state.get());
+ ASSERT_EQ("T01", continue_response.GetStringRef());
+}
+
+TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) {
+ StringExtractorGDBRemote continue_response, response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Interrupt should do nothing when we're not running.
+ ASSERT_FALSE(fix.client.Interrupt());
+
+ // Continue. After the run packet is sent, send an interrupt.
+ std::future<StateType> continue_state = std::async(
+ std::launch::async, [&] { return fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ std::future<bool> async_result =
+ std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
+
+ // We get interrupted.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("\x03", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.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;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Continue. After the run packet is sent, send an interrupt.
+ std::future<StateType> continue_state = std::async(
+ std::launch::async, [&] { return fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ std::future<bool> async_result =
+ std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
+
+ // However, the target stops due to a different reason than the original
+ // interrupt.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("\x03", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.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, fix.server.SendPacket("T01"));
+ ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
+ ASSERT_EQ("T01", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+}
+
+TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) {
+ StringExtractorGDBRemote continue_response, async_response, response;
+ const bool send_async = true;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Interrupt should do nothing when we're not running.
+ ASSERT_FALSE(fix.client.Interrupt());
+
+ // Continue. After the run packet is sent, send an async signal.
+ std::future<StateType> continue_state = std::async(
+ std::launch::async, [&] { return fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ std::future<bool> interrupt_result =
+ std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
+
+ // We get interrupted. We'll send two packets to simulate a buggy stub.
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("\x03", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
+ ASSERT_EQ(PacketResult::Success, fix.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 fix.client.SendPacketAndWaitForResponse("qTest", async_response,
+ !send_async);
+ });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("qTest", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest"));
+ ASSERT_EQ(PacketResult::Success, send_result.get());
+ ASSERT_EQ("QTest", async_response.GetStringRef());
+}
+
+TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateInterface) {
+ StringExtractorGDBRemote response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Continue. We'll have the server send a bunch of async packets before it
+ // stops.
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4142"));
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Apro"));
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4344"));
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Afile"));
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
+ ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
+ ASSERT_EQ("T01", response.GetStringRef());
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+
+ EXPECT_EQ("ABCD", fix.delegate.output);
+ EXPECT_EQ("profile", fix.delegate.misc_data);
+ EXPECT_EQ(1u, fix.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();
+
+ // Set up the
+ StringExtractorGDBRemote response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Send async structured data packet, then stop.
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket(stream.GetData()));
+ ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
+ ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
+ ASSERT_EQ("T01", response.GetStringRef());
+ ASSERT_EQ(1ul, fix.delegate.structured_data_packets.size());
+
+ // Verify the packet contents. It should have been unescaped upon packet
+ // reception.
+ ASSERT_EQ(json_packet, fix.delegate.structured_data_packets[0]);
+}
+
+TEST_F(GDBRemoteClientBaseTest, InterruptNoResponse) {
+ StringExtractorGDBRemote continue_response, response;
+ ContinueFixture fix;
+ if (HasFailure())
+ return;
+
+ // Continue. After the run packet is sent, send an interrupt.
+ std::future<StateType> continue_state = std::async(
+ std::launch::async, [&] { return fix.SendCPacket(continue_response); });
+ ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
+ ASSERT_EQ("c", response.GetStringRef());
+ fix.WaitForRunEvent();
+
+ std::future<bool> async_result =
+ std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
+
+ // We get interrupted, but we don't send a stop packet.
+ ASSERT_EQ(PacketResult::Success, fix.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());
+}
diff --git a/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
new file mode 100644
index 000000000000..84b354d75170
--- /dev/null
+++ b/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -0,0 +1,307 @@
+//===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
+// Workaround for MSVC standard library bug, which fails to include <thread>
+// when
+// exceptions are disabled.
+#include <eh.h>
+#endif
+#include <future>
+
+#include "GDBRemoteTestUtils.h"
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
+#include "lldb/Core/DataBuffer.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/StructuredData.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+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, StringRef expected, StringRef response) {
+ StringExtractorGDBRemote request;
+ ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
+ ASSERT_EQ(expected, request.GetStringRef());
+ 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 {};
+
+TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ 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) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ 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) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ 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) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ 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) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ 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) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ llvm::Triple triple("i386-pc-linux");
+
+ FileSpec file_specs[] = {
+ FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix),
+ FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix)};
+ 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"([{"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("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID());
+ EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset());
+ EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize());
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ llvm::Triple triple("i386-pc-linux");
+ FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix);
+
+ const char *invalid_responses[] = {
+ "OK", "E47", "[]",
+ // no UUID
+ R"([{"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);
+
+ ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response;
+ }
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
+ TestClient client;
+ MockServer server;
+ Connect(client, server);
+ if (HasFailure())
+ return;
+
+ std::thread server_thread([&server] {
+ 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();
+
+ 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);
+}
diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp
new file mode 100644
index 000000000000..58cc9f50586a
--- /dev/null
+++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp
@@ -0,0 +1,69 @@
+//===-- GDBRemoteTestUtils.cpp ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
+// Workaround for MSVC standard library bug, which fails to include <thread>
+// when
+// exceptions are disabled.
+#include <eh.h>
+#endif
+
+#include "GDBRemoteTestUtils.h"
+
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+
+#include <future>
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+void GDBRemoteTest::SetUpTestCase() {
+#if defined(_MSC_VER)
+ WSADATA data;
+ ::WSAStartup(MAKEWORD(2, 2), &data);
+#endif
+}
+
+void GDBRemoteTest::TearDownTestCase() {
+#if defined(_MSC_VER)
+ ::WSACleanup();
+#endif
+}
+
+void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server) {
+ bool child_processes_inherit = false;
+ Error error;
+ TCPSocket listen_socket(child_processes_inherit, error);
+ ASSERT_FALSE(error.Fail());
+ error = listen_socket.Listen("127.0.0.1:0", 5);
+ ASSERT_FALSE(error.Fail());
+
+ Socket *accept_socket;
+ std::future<Error> accept_error = std::async(std::launch::async, [&] {
+ return listen_socket.Accept("127.0.0.1:0", child_processes_inherit,
+ accept_socket);
+ });
+
+ char connect_remote_address[64];
+ snprintf(connect_remote_address, sizeof(connect_remote_address),
+ "connect://localhost:%u", listen_socket.GetLocalPortNumber());
+
+ std::unique_ptr<ConnectionFileDescriptor> conn_ap(
+ new ConnectionFileDescriptor());
+ ASSERT_EQ(conn_ap->Connect(connect_remote_address, nullptr),
+ lldb::eConnectionStatusSuccess);
+
+ client.SetConnection(conn_ap.release());
+ ASSERT_TRUE(accept_error.get().Success());
+ server.SetConnection(new ConnectionFileDescriptor(accept_socket));
+}
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
diff --git a/unittests/Process/gdb-remote/GDBRemoteTestUtils.h b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h
new file mode 100644
index 000000000000..d7700f23e5ab
--- /dev/null
+++ b/unittests/Process/gdb-remote/GDBRemoteTestUtils.h
@@ -0,0 +1,51 @@
+//===-- GDBRemoteTestUtils.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h
+#define lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h
+
+#include "gtest/gtest.h"
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteTest : public testing::Test {
+public:
+ static void SetUpTestCase();
+
+ static void TearDownTestCase();
+};
+
+void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server);
+
+struct MockServer : public GDBRemoteCommunicationServer {
+ MockServer()
+ : GDBRemoteCommunicationServer("mock-server", "mock-server.listener") {
+ m_send_acks = false;
+ }
+
+ 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::SendOKResponse;
+ using GDBRemoteCommunicationServer::SendUnimplementedResponse;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // lldb_unittests_Process_gdb_remote_GDBRemoteTestUtils_h
diff --git a/unittests/Process/minidump/CMakeLists.txt b/unittests/Process/minidump/CMakeLists.txt
new file mode 100644
index 000000000000..10cb8c34f352
--- /dev/null
+++ b/unittests/Process/minidump/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_lldb_unittest(LLDBMinidumpTests
+ MinidumpParserTest.cpp
+ )
+
+set(test_inputs
+ linux-i386.dmp
+ linux-x86_64.dmp
+ linux-x86_64_not_crashed.dmp
+ fizzbuzz_no_heap.dmp
+ fizzbuzz_wow64.dmp)
+
+add_unittest_inputs(LLDBMinidumpTests "${test_inputs}")
diff --git a/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp b/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp
new file mode 100644
index 000000000000..19008c91fc3e
--- /dev/null
+++ b/unittests/Process/minidump/Inputs/fizzbuzz_no_heap.dmp
Binary files differ
diff --git a/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp b/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp
new file mode 100644
index 000000000000..3d97186f2cd2
--- /dev/null
+++ b/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp
Binary files differ
diff --git a/unittests/Process/minidump/Inputs/linux-i386.dmp b/unittests/Process/minidump/Inputs/linux-i386.dmp
new file mode 100644
index 000000000000..946a7cf0355b
--- /dev/null
+++ b/unittests/Process/minidump/Inputs/linux-i386.dmp
Binary files differ
diff --git a/unittests/Process/minidump/Inputs/linux-x86_64.cpp b/unittests/Process/minidump/Inputs/linux-x86_64.cpp
new file mode 100644
index 000000000000..827fe67b503b
--- /dev/null
+++ b/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/unittests/Process/minidump/Inputs/linux-x86_64.dmp b/unittests/Process/minidump/Inputs/linux-x86_64.dmp
new file mode 100644
index 000000000000..29a12d6a2ebc
--- /dev/null
+++ b/unittests/Process/minidump/Inputs/linux-x86_64.dmp
Binary files differ
diff --git a/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp b/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp
new file mode 100644
index 000000000000..ad4b61a7bbb4
--- /dev/null
+++ b/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp
Binary files differ
diff --git a/unittests/Process/minidump/MinidumpParserTest.cpp b/unittests/Process/minidump/MinidumpParserTest.cpp
new file mode 100644
index 000000000000..83225e88ee03
--- /dev/null
+++ b/unittests/Process/minidump/MinidumpParserTest.cpp
@@ -0,0 +1,453 @@
+//===-- MinidumpTypesTest.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Project includes
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+#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"
+
+// Other libraries and framework includes
+#include "gtest/gtest.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+// C includes
+
+// C++ includes
+#include <memory>
+
+extern const char *TestMainArgv0;
+
+using namespace lldb_private;
+using namespace minidump;
+
+class MinidumpParserTest : public testing::Test {
+public:
+ void SetUp() override {
+ llvm::StringRef dmp_folder = llvm::sys::path::parent_path(TestMainArgv0);
+ inputs_folder = dmp_folder;
+ llvm::sys::path::append(inputs_folder, "Inputs");
+ }
+
+ void SetUpData(const char *minidump_filename, size_t load_size = SIZE_MAX) {
+ llvm::SmallString<128> filename = inputs_folder;
+ llvm::sys::path::append(filename, minidump_filename);
+ FileSpec minidump_file(filename.c_str(), false);
+ lldb::DataBufferSP data_sp(
+ minidump_file.MemoryMapFileContents(0, load_size));
+ llvm::Optional<MinidumpParser> optional_parser =
+ MinidumpParser::Create(data_sp);
+ ASSERT_TRUE(optional_parser.hasValue());
+ parser.reset(new MinidumpParser(optional_parser.getValue()));
+ ASSERT_GT(parser->GetData().size(), 0UL);
+ }
+
+ llvm::SmallString<128> inputs_folder;
+ std::unique_ptr<MinidumpParser> parser;
+};
+
+TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) {
+ SetUpData("linux-x86_64.dmp");
+ llvm::ArrayRef<MinidumpThread> thread_list;
+
+ thread_list = parser->GetThreads();
+ ASSERT_EQ(1UL, thread_list.size());
+
+ const MinidumpThread thread = thread_list[0];
+
+ EXPECT_EQ(16001UL, thread.thread_id);
+
+ llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread);
+ EXPECT_EQ(1232UL, context.size());
+}
+
+TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) {
+ SetUpData("linux-x86_64.dmp", 200);
+ llvm::ArrayRef<MinidumpThread> thread_list;
+
+ thread_list = parser->GetThreads();
+ ASSERT_EQ(0UL, thread_list.size());
+}
+
+TEST_F(MinidumpParserTest, GetArchitecture) {
+ SetUpData("linux-x86_64.dmp");
+ ASSERT_EQ(llvm::Triple::ArchType::x86_64,
+ parser->GetArchitecture().GetMachine());
+ ASSERT_EQ(llvm::Triple::OSType::Linux,
+ parser->GetArchitecture().GetTriple().getOS());
+}
+
+TEST_F(MinidumpParserTest, GetMiscInfo) {
+ SetUpData("linux-x86_64.dmp");
+ const MinidumpMiscInfo *misc_info = parser->GetMiscInfo();
+ ASSERT_EQ(nullptr, misc_info);
+}
+
+TEST_F(MinidumpParserTest, GetLinuxProcStatus) {
+ SetUpData("linux-x86_64.dmp");
+ 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) {
+ SetUpData("linux-x86_64.dmp");
+ llvm::Optional<lldb::pid_t> pid = parser->GetPid();
+ ASSERT_TRUE(pid.hasValue());
+ ASSERT_EQ(16001UL, pid.getValue());
+}
+
+TEST_F(MinidumpParserTest, GetModuleList) {
+ SetUpData("linux-x86_64.dmp");
+ llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
+ ASSERT_EQ(8UL, modules.size());
+ std::string module_names[8] = {
+ "/usr/local/google/home/dvlahovski/projects/test_breakpad/a.out",
+ "/lib/x86_64-linux-gnu/libm-2.19.so",
+ "/lib/x86_64-linux-gnu/libc-2.19.so",
+ "/lib/x86_64-linux-gnu/libgcc_s.so.1",
+ "/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19",
+ "/lib/x86_64-linux-gnu/libpthread-2.19.so",
+ "/lib/x86_64-linux-gnu/ld-2.19.so",
+ "linux-gate.so",
+ };
+
+ for (int i = 0; i < 8; ++i) {
+ llvm::Optional<std::string> name =
+ parser->GetMinidumpString(modules[i].module_name_rva);
+ ASSERT_TRUE(name.hasValue());
+ EXPECT_EQ(module_names[i], name.getValue());
+ }
+}
+
+TEST_F(MinidumpParserTest, GetFilteredModuleList) {
+ SetUpData("linux-x86_64_not_crashed.dmp");
+ llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
+ std::vector<const MinidumpModule *> filtered_modules =
+ parser->GetFilteredModuleList();
+ EXPECT_EQ(10UL, modules.size());
+ EXPECT_EQ(9UL, filtered_modules.size());
+ // EXPECT_GT(modules.size(), filtered_modules.size());
+ bool found = false;
+ for (size_t i = 0; i < filtered_modules.size(); ++i) {
+ llvm::Optional<std::string> name =
+ parser->GetMinidumpString(filtered_modules[i]->module_name_rva);
+ ASSERT_TRUE(name.hasValue());
+ if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") {
+ ASSERT_FALSE(found) << "There should be only one module with this name "
+ "in the filtered module list";
+ found = true;
+ ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image);
+ }
+ }
+}
+
+TEST_F(MinidumpParserTest, GetExceptionStream) {
+ SetUpData("linux-x86_64.dmp");
+ const MinidumpExceptionStream *exception_stream =
+ parser->GetExceptionStream();
+ ASSERT_NE(nullptr, exception_stream);
+ ASSERT_EQ(11UL, exception_stream->exception_record.exception_code);
+}
+
+void check_mem_range_exists(std::unique_ptr<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) {
+ SetUpData("linux-x86_64.dmp");
+ // There are two memory ranges in the file (size is in bytes, decimal):
+ // 1) 0x401d46 256
+ // 2) 0x7ffceb34a000 12288
+ EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue());
+ EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue());
+
+ check_mem_range_exists(parser, 0x401d46, 256);
+ EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue());
+
+ check_mem_range_exists(parser, 0x7ffceb34a000, 12288);
+ EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue());
+}
+
+TEST_F(MinidumpParserTest, GetMemory) {
+ SetUpData("linux-x86_64.dmp");
+
+ EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size());
+ EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size());
+
+ EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size());
+ EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size());
+
+ EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty());
+}
+
+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());
+}
+
+void check_region_info(std::unique_ptr<MinidumpParser> &parser,
+ const uint64_t addr, MemoryRegionInfo::OptionalBool read,
+ MemoryRegionInfo::OptionalBool write,
+ MemoryRegionInfo::OptionalBool exec) {
+ auto range_info = parser->GetMemoryRegionInfo(addr);
+ ASSERT_TRUE(range_info.hasValue());
+ EXPECT_EQ(read, range_info->GetReadable());
+ EXPECT_EQ(write, range_info->GetWritable());
+ EXPECT_EQ(exec, range_info->GetExecutable());
+}
+
+TEST_F(MinidumpParserTest, GetMemoryRegionInfo) {
+ SetUpData("fizzbuzz_wow64.dmp");
+
+ const auto yes = MemoryRegionInfo::eYes;
+ const auto no = MemoryRegionInfo::eNo;
+
+ check_region_info(parser, 0x00000, no, no, no);
+ check_region_info(parser, 0x10000, yes, yes, no);
+ check_region_info(parser, 0x20000, yes, yes, no);
+ check_region_info(parser, 0x30000, yes, yes, no);
+ check_region_info(parser, 0x31000, no, no, no);
+ check_region_info(parser, 0x40000, yes, no, no);
+}
+
+// Windows Minidump tests
+// fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests
+TEST_F(MinidumpParserTest, GetArchitectureWindows) {
+ SetUpData("fizzbuzz_no_heap.dmp");
+ ASSERT_EQ(llvm::Triple::ArchType::x86,
+ parser->GetArchitecture().GetMachine());
+ ASSERT_EQ(llvm::Triple::OSType::Win32,
+ parser->GetArchitecture().GetTriple().getOS());
+}
+
+TEST_F(MinidumpParserTest, GetLinuxProcStatusWindows) {
+ SetUpData("fizzbuzz_no_heap.dmp");
+ llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus();
+ ASSERT_FALSE(proc_status.hasValue());
+}
+
+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());
+}
+
+TEST_F(MinidumpParserTest, GetModuleListWow64) {
+ SetUpData("fizzbuzz_wow64.dmp");
+ llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
+ ASSERT_EQ(16UL, modules.size());
+ std::string module_names[16] = {
+ R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)",
+ R"(C:\Windows\System32\ntdll.dll)",
+ R"(C:\Windows\System32\wow64.dll)",
+ R"(C:\Windows\System32\wow64win.dll)",
+ R"(C:\Windows\System32\wow64cpu.dll)",
+ R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)",
+ R"(C:\Windows\SysWOW64\ntdll.dll)",
+ R"(C:\Windows\SysWOW64\kernel32.dll)",
+ R"(C:\Windows\SysWOW64\KERNELBASE.dll)",
+ R"(C:\Windows\SysWOW64\advapi32.dll)",
+ R"(C:\Windows\SysWOW64\msvcrt.dll)",
+ R"(C:\Windows\SysWOW64\sechost.dll)",
+ R"(C:\Windows\SysWOW64\rpcrt4.dll)",
+ R"(C:\Windows\SysWOW64\sspicli.dll)",
+ R"(C:\Windows\SysWOW64\CRYPTBASE.dll)",
+ R"(C:\Windows\System32\api-ms-win-core-synch-l1-2-0.DLL)",
+ };
+
+ for (int i = 0; i < 16; ++i) {
+ llvm::Optional<std::string> name =
+ parser->GetMinidumpString(modules[i].module_name_rva);
+ ASSERT_TRUE(name.hasValue());
+ EXPECT_EQ(module_names[i], name.getValue());
+ }
+}
+
+// Register tests
+#define REG_VAL32(x) *(reinterpret_cast<uint32_t *>(x))
+#define REG_VAL64(x) *(reinterpret_cast<uint64_t *>(x))
+
+TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) {
+ SetUpData("linux-i386.dmp");
+ llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
+ const MinidumpThread thread = thread_list[0];
+ llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread));
+
+ ArchSpec arch = parser->GetArchitecture();
+ RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_32(registers, reg_interface);
+ ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
+
+ const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
+
+ std::map<uint64_t, uint32_t> reg_values;
+
+ reg_values[lldb_eax_i386] = 0x00000000;
+ reg_values[lldb_ebx_i386] = 0xf7778000;
+ reg_values[lldb_ecx_i386] = 0x00000001;
+ reg_values[lldb_edx_i386] = 0xff9dd4a3;
+ reg_values[lldb_edi_i386] = 0x080482a8;
+ reg_values[lldb_esi_i386] = 0xff9dd55c;
+ reg_values[lldb_ebp_i386] = 0xff9dd53c;
+ reg_values[lldb_esp_i386] = 0xff9dd52c;
+ reg_values[lldb_eip_i386] = 0x080482a0;
+ reg_values[lldb_eflags_i386] = 0x00010282;
+ reg_values[lldb_cs_i386] = 0x00000023;
+ reg_values[lldb_fs_i386] = 0x00000000;
+ reg_values[lldb_gs_i386] = 0x00000063;
+ reg_values[lldb_ss_i386] = 0x0000002b;
+ reg_values[lldb_ds_i386] = 0x0000002b;
+ reg_values[lldb_es_i386] = 0x0000002b;
+
+ for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
+ ++reg_index) {
+ if (reg_values.find(reg_index) != reg_values.end()) {
+ EXPECT_EQ(reg_values[reg_index],
+ REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset));
+ }
+ }
+}
+
+TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_64) {
+ SetUpData("linux-x86_64.dmp");
+ llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
+ const MinidumpThread thread = thread_list[0];
+ llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread));
+
+ ArchSpec arch = parser->GetArchitecture();
+ RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_64(registers, reg_interface);
+ ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
+
+ const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
+
+ std::map<uint64_t, uint64_t> reg_values;
+
+ reg_values[lldb_rax_x86_64] = 0x0000000000000000;
+ reg_values[lldb_rbx_x86_64] = 0x0000000000000000;
+ reg_values[lldb_rcx_x86_64] = 0x0000000000000010;
+ reg_values[lldb_rdx_x86_64] = 0x0000000000000000;
+ reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0;
+ reg_values[lldb_rsi_x86_64] = 0x0000000000000000;
+ reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210;
+ reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210;
+ reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0;
+ reg_values[lldb_r9_x86_64] = 0x0000000000000000;
+ reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0;
+ reg_values[lldb_r11_x86_64] = 0x0000000000000246;
+ reg_values[lldb_r12_x86_64] = 0x0000000000401c92;
+ reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430;
+ reg_values[lldb_r14_x86_64] = 0x0000000000000000;
+ reg_values[lldb_r15_x86_64] = 0x0000000000000000;
+ reg_values[lldb_rip_x86_64] = 0x0000000000401dc6;
+ reg_values[lldb_rflags_x86_64] = 0x0000000000010206;
+ reg_values[lldb_cs_x86_64] = 0x0000000000000033;
+ reg_values[lldb_fs_x86_64] = 0x0000000000000000;
+ reg_values[lldb_gs_x86_64] = 0x0000000000000000;
+ reg_values[lldb_ss_x86_64] = 0x0000000000000000;
+ reg_values[lldb_ds_x86_64] = 0x0000000000000000;
+ reg_values[lldb_es_x86_64] = 0x0000000000000000;
+
+ for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
+ ++reg_index) {
+ if (reg_values.find(reg_index) != reg_values.end()) {
+ EXPECT_EQ(reg_values[reg_index],
+ REG_VAL64(buf->GetBytes() + reg_info[reg_index].byte_offset));
+ }
+ }
+}
+
+TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32_wow64) {
+ SetUpData("fizzbuzz_wow64.dmp");
+ llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
+ const MinidumpThread thread = thread_list[0];
+ llvm::ArrayRef<uint8_t> registers(parser->GetThreadContextWow64(thread));
+
+ ArchSpec arch = parser->GetArchitecture();
+ RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_32(registers, reg_interface);
+ ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
+
+ const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
+
+ std::map<uint64_t, uint32_t> reg_values;
+
+ reg_values[lldb_eax_i386] = 0x00000000;
+ reg_values[lldb_ebx_i386] = 0x0037f608;
+ reg_values[lldb_ecx_i386] = 0x00e61578;
+ reg_values[lldb_edx_i386] = 0x00000008;
+ reg_values[lldb_edi_i386] = 0x00000000;
+ reg_values[lldb_esi_i386] = 0x00000002;
+ reg_values[lldb_ebp_i386] = 0x0037f654;
+ reg_values[lldb_esp_i386] = 0x0037f5b8;
+ reg_values[lldb_eip_i386] = 0x77ce01fd;
+ reg_values[lldb_eflags_i386] = 0x00000246;
+ reg_values[lldb_cs_i386] = 0x00000023;
+ reg_values[lldb_fs_i386] = 0x00000053;
+ reg_values[lldb_gs_i386] = 0x0000002b;
+ reg_values[lldb_ss_i386] = 0x0000002b;
+ reg_values[lldb_ds_i386] = 0x0000002b;
+ reg_values[lldb_es_i386] = 0x0000002b;
+
+ for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
+ ++reg_index) {
+ if (reg_values.find(reg_index) != reg_values.end()) {
+ EXPECT_EQ(reg_values[reg_index],
+ REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset));
+ }
+ }
+} \ No newline at end of file