jmajors updated this revision to Diff 100178. jmajors added a comment. Format changes.
https://reviews.llvm.org/D32930 Files: unittests/CMakeLists.txt unittests/tools/CMakeLists.txt unittests/tools/lldb-server/CMakeLists.txt unittests/tools/lldb-server/inferior/thread_inferior.cpp unittests/tools/lldb-server/tests/CMakeLists.txt unittests/tools/lldb-server/tests/MessageObjects.cpp unittests/tools/lldb-server/tests/MessageObjects.h unittests/tools/lldb-server/tests/TestClient.cpp unittests/tools/lldb-server/tests/TestClient.h unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp
Index: unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp @@ -0,0 +1,58 @@ +//===-- ThreadsInJstopinfoTest.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestClient.h" +#include "gtest/gtest.h" +#include <string> + +using namespace llgs_tests; + +class ThreadsInJstopinfoTest : public ::testing::Test { +protected: + virtual void SetUp() { TestClient::Initialize(); } +}; + +TEST_F(ThreadsInJstopinfoTest, TestStopReplyContainsThreadPcsLlgs) { + std::vector<std::string> inferior_args; + // This inferior spawns N threads, then forces a break. + inferior_args.push_back(THREAD_INFERIOR); + inferior_args.push_back("4"); + + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + + TestClient client(test_info->name(), test_info->test_case_name()); + ASSERT_TRUE(client.StartDebugger()); + ASSERT_TRUE(client.SetInferior(inferior_args)); + ASSERT_TRUE(client.ListThreadsInStopReply()); + ASSERT_TRUE(client.ContinueAll()); + unsigned int pc_reg = client.GetPcRegisterId(); + ASSERT_NE(pc_reg, UINT_MAX); + + auto jthreads_info = client.GetJThreadsInfo(); + ASSERT_TRUE(jthreads_info); + + auto stop_reply = client.GetLatestStopReply(); + auto stop_reply_pcs = stop_reply.GetThreadPcs(); + auto thread_infos = jthreads_info->GetThreadInfos(); + ASSERT_EQ(stop_reply_pcs.size(), thread_infos.size()) + << "Thread count mismatch."; + + for (auto stop_reply_pc : stop_reply_pcs) { + unsigned long tid = stop_reply_pc.first; + ASSERT_TRUE(thread_infos.find(tid) != thread_infos.end()) + << "Thread ID: " << tid << " not in JThreadsInfo."; + uint64_t pc_value; + ASSERT_TRUE(thread_infos[tid].ReadRegisterAsUint64(pc_reg, pc_value)) + << "Failure reading ThreadInfo register " << pc_reg; + ASSERT_EQ(stop_reply_pcs[tid], pc_value) + << "Mismatched PC for thread: " << tid; + } + + ASSERT_TRUE(client.StopDebugger()); +} Index: unittests/tools/lldb-server/tests/TestClient.h =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/TestClient.h @@ -0,0 +1,61 @@ +//===-- TestClient.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MessageObjects.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/Optional.h" +#include <memory> +#include <string> + +namespace llgs_tests { +// TODO: Make the test client an abstract base class, with different children +// for different types of connections: llgs v. debugserver +class TestClient + : public lldb_private::process_gdb_remote::GDBRemoteCommunicationClient { +public: + static void Initialize(); + TestClient(const std::string &test_name, const std::string &test_case_name); + virtual ~TestClient(); + LLVM_NODISCARD bool StartDebugger(); + LLVM_NODISCARD bool StopDebugger(); + LLVM_NODISCARD bool SetInferior(llvm::ArrayRef<std::string> inferior_args); + LLVM_NODISCARD bool ListThreadsInStopReply(); + LLVM_NODISCARD bool SetBreakpoint(unsigned long address); + LLVM_NODISCARD bool ContinueAll(); + LLVM_NODISCARD bool ContinueThread(unsigned long thread_id); + const ProcessInfo &GetProcessInfo(); + llvm::Optional<JThreadsInfo> GetJThreadsInfo(); + const StopReply &GetLatestStopReply(); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message, + std::string &response_string); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message, + std::string &response_string, + PacketResult expected_result); + unsigned int GetPcRegisterId(); + +private: + LLVM_NODISCARD bool Continue(llvm::StringRef message); + LLVM_NODISCARD bool GenerateConnectionAddress(std::string &address); + std::string GenerateLogFileName(const lldb_private::ArchSpec &arch) const; + std::string FormatFailedResult( + const std::string &message, + lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult + result); + + llvm::Optional<ProcessInfo> m_process_info; + llvm::Optional<StopReply> m_stop_reply; + lldb_private::ProcessLaunchInfo m_server_process_info; + std::string m_test_name; + std::string m_test_case_name; + unsigned int m_pc_register; +}; +} // namespace llgs_tests Index: unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/TestClient.cpp @@ -0,0 +1,290 @@ +//===-- TestClient.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestClient.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/posix/ProcessLauncherPosix.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" +#include <cstdlib> +#include <future> +#include <sstream> +#include <string> +#include <sys/stat.h> +#include <sys/types.h> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace llgs_tests { +void TestClient::Initialize() { HostInfo::Initialize(); } + +TestClient::TestClient(const std::string &test_name, + const std::string &test_case_name) + : m_test_name(test_name), m_test_case_name(test_case_name), + m_pc_register(UINT_MAX) {} + +TestClient::~TestClient() {} + +bool TestClient::StartDebugger() { + const ArchSpec &arch_spec = HostInfo::GetArchitecture(); + Args args; + args.AppendArgument(LLDB_SERVER); + args.AppendArgument("gdbserver"); + args.AppendArgument("--log-channels=gdb-remote packets"); + args.AppendArgument("--reverse-connect"); + std::string log_file_name = GenerateLogFileName(arch_spec); + if (log_file_name.size()) { + args.AppendArgument("--log-file=" + log_file_name); + } + + Status error; + TCPSocket listen_socket(true, false); + error = listen_socket.Listen("127.0.0.1:0", 5); + if (error.Fail()) { + GTEST_LOG_(ERROR) << "Unable to open listen socket."; + return false; + } + + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), + "localhost:%u", listen_socket.GetLocalPortNumber()); + + args.AppendArgument(connect_remote_address); + + m_server_process_info.SetArchitecture(arch_spec); + m_server_process_info.SetArguments(args, true); + Status status = Host::LaunchProcess(m_server_process_info); + if (status.Fail()) { + GTEST_LOG_(ERROR) + << formatv("Failure to launch lldb server: {0}.", status).str(); + return false; + } + + char connect_remote_uri[64]; + snprintf(connect_remote_uri, sizeof(connect_remote_uri), "connect://%s", + connect_remote_address); + Socket *accept_socket; + listen_socket.Accept(accept_socket); + SetConnection(new ConnectionFileDescriptor(accept_socket)); + + SendAck(); // Send this as a handshake. + return true; +} + +bool TestClient::StopDebugger() { + std::string response; + return SendMessage("k", response, PacketResult::ErrorDisconnected); +} + +bool TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { + std::stringstream command; + command << "A"; + for (size_t i = 0; i < inferior_args.size(); i++) { + if (i > 0) + command << ','; + std::string hex_encoded = toHex(inferior_args[i]); + command << hex_encoded.size() << ',' << i << ',' << hex_encoded; + } + + if (!SendMessage(command.str())) + return false; + if (!SendMessage("qLaunchSuccess")) + return false; + std::string response; + if (!SendMessage("qProcessInfo", response)) + return false; + auto create_or_error = ProcessInfo::Create(response); + if (auto create_error = create_or_error.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return false; + } + + m_process_info = *create_or_error; + return true; +} + +bool TestClient::ListThreadsInStopReply() { + return SendMessage("QListThreadsInStopReply"); +} + +bool TestClient::SetBreakpoint(unsigned long address) { + std::stringstream command; + command << "Z0," << std::hex << address << ",1"; + return SendMessage(command.str()); +} + +bool TestClient::ContinueAll() { return Continue("vCont;c"); } + +bool TestClient::ContinueThread(unsigned long thread_id) { + return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); +} + +const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; } + +Optional<JThreadsInfo> TestClient::GetJThreadsInfo() { + std::string response; + if (!SendMessage("jThreadsInfo", response)) + return llvm::None; + auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); + if (auto create_error = creation.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return llvm::None; + } + + return std::move(*creation); +} + +const StopReply &TestClient::GetLatestStopReply() { + return m_stop_reply.getValue(); +} + +bool TestClient::SendMessage(StringRef message) { + std::string dummy_string; + return SendMessage(message, dummy_string); +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string) { + if (!SendMessage(message, response_string, PacketResult::Success)) + return false; + else if (response_string[0] == 'E') { + GTEST_LOG_(ERROR) << "Error " << response_string + << " while sending message: " << message.str(); + return false; + } + + return true; +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string, + PacketResult expected_result) { + StringExtractorGDBRemote response; + GTEST_LOG_(INFO) << "Send Packet: " << message.str(); + PacketResult result = SendPacketAndWaitForResponse(message, response, false); + response.GetEscapedBinaryData(response_string); + GTEST_LOG_(INFO) << "Read Packet: " << response_string; + if (result != expected_result) { + GTEST_LOG_(ERROR) << FormatFailedResult(message, result); + return false; + } + + return true; +} + +unsigned int TestClient::GetPcRegisterId() { + if (m_pc_register != UINT_MAX) + return m_pc_register; + + for (unsigned int register_id = 0;; register_id++) { + std::string message = formatv("qRegisterInfo{0:x-}", register_id).str(); + std::string response; + if (!SendMessage(message, response)) { + GTEST_LOG_(ERROR) << "Unable to query register ID for PC register."; + return UINT_MAX; + } + + auto elements_or_error = SplitPairList("GetPcRegisterId", response); + if (auto split_error = elements_or_error.takeError()) { + GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " + << response; + return UINT_MAX; + } + + auto elements = *elements_or_error; + if (elements["alt-name"] == "pc" || elements["generic"] == "pc") { + m_pc_register = register_id; + break; + } + } + + return m_pc_register; +} + +bool TestClient::Continue(StringRef message) { + if (!m_process_info.hasValue()) { + GTEST_LOG_(ERROR) << "Continue() called before m_process_info initialized."; + return false; + } + + std::string response; + if (!SendMessage(message, response)) + return false; + auto creation = StopReply::Create(response, m_process_info->GetEndian()); + if (auto create_error = creation.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return false; + } + + m_stop_reply = std::move(*creation); + return true; +} + +std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const { + char *log_directory = getenv("LOG_FILE_DIRECTORY"); + if (!log_directory) + return ""; + + struct stat info; + if (stat(log_directory, &info) != 0 || !(info.st_mode & S_IFDIR)) { + GTEST_LOG_(WARNING) << "Cannot access log directory: " << log_directory; + return ""; + } + + std::string log_file_name; + raw_string_ostream log_file(log_file_name); + log_file << log_directory << "/lldb-" << m_test_case_name << '-' + << m_test_name << '-' << arch.GetArchitectureName() << ".log"; + return log_file.str(); +} + +std::string TestClient::FormatFailedResult(const std::string &message, + PacketResult result) { + std::string formatted_error; + raw_string_ostream error_stream(formatted_error); + error_stream << "Failure sending message: " << message << " Result: "; + + switch (result) { + case PacketResult::ErrorSendFailed: + error_stream << "ErrorSendFailed"; + break; + case PacketResult::ErrorSendAck: + error_stream << "ErrorSendAck"; + break; + case PacketResult::ErrorReplyFailed: + error_stream << "ErrorReplyFailed"; + break; + case PacketResult::ErrorReplyTimeout: + error_stream << "ErrorReplyTimeout"; + break; + case PacketResult::ErrorReplyInvalid: + error_stream << "ErrorReplyInvalid"; + break; + case PacketResult::ErrorReplyAck: + error_stream << "ErrorReplyAck"; + break; + case PacketResult::ErrorDisconnected: + error_stream << "ErrorDisconnected"; + break; + case PacketResult::ErrorNoSequenceLock: + error_stream << "ErrorNoSequenceLock"; + break; + default: + error_stream << "Unknown Error"; + } + + error_stream.str(); + return formatted_error; +} +} // namespace llgs_tests Index: unittests/tools/lldb-server/tests/MessageObjects.h =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/MessageObjects.h @@ -0,0 +1,102 @@ +//===-- MessageObjects.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-types.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include <string> + +namespace llgs_tests { +class ThreadInfo; +typedef llvm::DenseMap<uint64_t, ThreadInfo> ThreadInfoMap; +typedef llvm::DenseMap<uint64_t, uint64_t> U64Map; +typedef llvm::DenseMap<unsigned int, std::string> RegisterMap; + +class ProcessInfo { +public: + static llvm::Expected<ProcessInfo> Create(llvm::StringRef response); + lldb::pid_t GetPid() const; + llvm::support::endianness GetEndian() const; + +private: + ProcessInfo() = default; + lldb::pid_t m_pid; + lldb::pid_t m_parent_pid; + uint32_t m_real_uid; + uint32_t m_real_gid; + uint32_t m_effective_uid; + uint32_t m_effective_gid; + std::string m_triple; + llvm::SmallString<16> m_ostype; + llvm::support::endianness m_endian; + unsigned int m_ptrsize; +}; + +class ThreadInfo { +public: + ThreadInfo() = default; + ThreadInfo(llvm::StringRef name, llvm::StringRef reason, + const RegisterMap ®isters, unsigned int signal); + + llvm::StringRef ReadRegister(unsigned int register_id) const; + bool ReadRegisterAsUint64(unsigned int register_id, uint64_t &value) const; + +private: + std::string m_name; + std::string m_reason; + RegisterMap m_registers; + unsigned int m_signal; +}; + +class JThreadsInfo { +public: + static llvm::Expected<JThreadsInfo> Create(llvm::StringRef response, + llvm::support::endianness endian); + + const ThreadInfoMap &GetThreadInfos() const; + +private: + JThreadsInfo() = default; + ThreadInfoMap m_thread_infos; +}; + +class StopReply { +public: + static llvm::Expected<StopReply> Create(llvm::StringRef response, + llvm::support::endianness endian); + const U64Map &GetThreadPcs() const; + +private: + StopReply() = default; + void ParseResponse(llvm::StringRef response, + llvm::support::endianness endian); + unsigned int m_signal; + lldb::tid_t m_thread; + std::string m_name; + U64Map m_thread_pcs; + RegisterMap m_registers; + std::string m_reason; +}; + +// Common functions for parsing packet data. +llvm::Expected<llvm::StringMap<llvm::StringRef>> +SplitPairList(llvm::StringRef caller, llvm::StringRef s); + +template <typename... Args> +llvm::Error make_parsing_error(llvm::StringRef format, Args &&... args) { + std::string error = + "Unable to parse " + + llvm::formatv(format.data(), std::forward<Args>(args)...).str(); + return llvm::make_error<llvm::StringError>(error, + llvm::inconvertibleErrorCode()); +} +} // namespace llgs_tests Index: unittests/tools/lldb-server/tests/MessageObjects.cpp =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/MessageObjects.cpp @@ -0,0 +1,207 @@ +//===-- MessageObjects.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MessageObjects.h" +#include "lldb/Core/StructuredData.h" +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace llvm; +using namespace llvm::support; +namespace llgs_tests { + +Expected<ProcessInfo> ProcessInfo::Create(StringRef response) { + ProcessInfo process_info; + auto elements_or_error = SplitPairList("ProcessInfo", response); + if (!elements_or_error) + return elements_or_error.takeError(); + + auto &elements = *elements_or_error; + if (elements["pid"].getAsInteger(16, process_info.m_pid)) + return make_parsing_error("ProcessInfo: pid"); + if (elements["parent-pid"].getAsInteger(16, process_info.m_parent_pid)) + return make_parsing_error("ProcessInfo: parent-pid"); + if (elements["real-uid"].getAsInteger(16, process_info.m_real_uid)) + return make_parsing_error("ProcessInfo: real-uid"); + if (elements["real-gid"].getAsInteger(16, process_info.m_real_gid)) + return make_parsing_error("ProcessInfo: real-uid"); + if (elements["effective-uid"].getAsInteger(16, process_info.m_effective_uid)) + return make_parsing_error("ProcessInfo: effective-uid"); + if (elements["effective-gid"].getAsInteger(16, process_info.m_effective_gid)) + return make_parsing_error("ProcessInfo: effective-gid"); + if (elements["ptrsize"].getAsInteger(10, process_info.m_ptrsize)) + return make_parsing_error("ProcessInfo: ptrsize"); + + process_info.m_triple = fromHex(elements["triple"]); + StringRef endian_str = elements["endian"]; + if (endian_str == "little") + process_info.m_endian = support::little; + else if (endian_str == "big") + process_info.m_endian = support::big; + else + return make_parsing_error("ProcessInfo: endian"); + + return process_info; +} + +lldb::pid_t ProcessInfo::GetPid() const { return m_pid; } + +endianness ProcessInfo::GetEndian() const { return m_endian; } + +//====== ThreadInfo ============================================================ +ThreadInfo::ThreadInfo(StringRef name, StringRef reason, + const RegisterMap ®isters, unsigned int signal) + : m_name(name.str()), m_reason(reason.str()), m_registers(registers), + m_signal(signal) {} + +StringRef ThreadInfo::ReadRegister(unsigned int register_id) const { + return m_registers.lookup(register_id); +} + +bool ThreadInfo::ReadRegisterAsUint64(unsigned int register_id, + uint64_t &value) const { + StringRef value_str(m_registers.lookup(register_id)); + if (value_str.getAsInteger(16, value)) { + GTEST_LOG_(ERROR) + << formatv("ThreadInfo: Unable to parse register value at {0}.", + register_id) + .str(); + return false; + } + + sys::swapByteOrder(value); + return true; +} + +//====== JThreadsInfo ========================================================== +Expected<JThreadsInfo> JThreadsInfo::Create(StringRef response, + endianness endian) { + JThreadsInfo jthreads_info; + + StructuredData::ObjectSP json = StructuredData::ParseJSON(response); + StructuredData::Array *array = json->GetAsArray(); + if (!array) + return make_parsing_error("JThreadsInfo: JSON array"); + + for (size_t i = 0; i < array->GetSize(); i++) { + StructuredData::Dictionary *thread_info; + array->GetItemAtIndexAsDictionary(i, thread_info); + if (!thread_info) + return make_parsing_error("JThreadsInfo: JSON obj at {0}", i); + + StringRef name, reason; + thread_info->GetValueForKeyAsString("name", name); + thread_info->GetValueForKeyAsString("reason", reason); + uint64_t signal; + thread_info->GetValueForKeyAsInteger("signal", signal); + uint64_t tid; + thread_info->GetValueForKeyAsInteger("tid", tid); + + StructuredData::Dictionary *register_dict; + thread_info->GetValueForKeyAsDictionary("registers", register_dict); + if (!register_dict) + return make_parsing_error("JThreadsInfo: registers JSON obj"); + + RegisterMap registers; + + auto keys_obj = register_dict->GetKeys(); + auto keys = keys_obj->GetAsArray(); + for (size_t i = 0; i < keys->GetSize(); i++) { + StringRef key_str, value_str; + keys->GetItemAtIndexAsString(i, key_str); + register_dict->GetValueForKeyAsString(key_str, value_str); + unsigned int register_id; + if (key_str.getAsInteger(10, register_id)) + return make_parsing_error("JThreadsInfo: register key[{0}]", i); + + registers[register_id] = value_str.str(); + } + + jthreads_info.m_thread_infos[tid] = + ThreadInfo(name, reason, registers, signal); + } + + return jthreads_info; +} + +const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const { + return m_thread_infos; +} + +//====== StopReply ============================================================= +const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; } + +Expected<StopReply> StopReply::Create(StringRef response, + llvm::support::endianness endian) { + StopReply stop_reply; + + auto elements_or_error = SplitPairList("StopReply", response); + if (auto split_error = elements_or_error.takeError()) { + return std::move(split_error); + } + + auto elements = *elements_or_error; + stop_reply.m_name = elements["name"]; + stop_reply.m_reason = elements["reason"]; + + SmallVector<StringRef, 20> threads; + SmallVector<StringRef, 20> pcs; + elements["threads"].split(threads, ','); + elements["thread-pcs"].split(pcs, ','); + if (threads.size() != pcs.size()) + return make_parsing_error("StopReply: thread/PC count mismatch"); + + for (size_t i = 0; i < threads.size(); i++) { + lldb::tid_t thread_id; + uint64_t pc; + if (threads[i].getAsInteger(16, thread_id)) + return make_parsing_error("StopReply: thread ID at [{0}].", i); + if (pcs[i].getAsInteger(16, pc)) + return make_parsing_error("StopReply: thread PC at [{0}].", i); + + stop_reply.m_thread_pcs[thread_id] = pc; + } + + for (auto i = elements.begin(); i != elements.end(); i++) { + StringRef key = i->getKey(); + StringRef val = i->getValue(); + if (key.size() >= 9 && key[0] == 'T' && key.substr(3, 6) == "thread") { + if (val.getAsInteger(16, stop_reply.m_thread)) + return make_parsing_error("StopReply: thread id"); + if (key.substr(1, 2).getAsInteger(16, stop_reply.m_signal)) + return make_parsing_error("StopReply: stop signal"); + } else if (key.size() == 2) { + unsigned int reg; + if (!key.getAsInteger(16, reg)) { + stop_reply.m_registers[reg] = val.str(); + } + } + } + + return stop_reply; +} + +//====== Globals =============================================================== +Expected<StringMap<StringRef>> SplitPairList(StringRef caller, StringRef str) { + SmallVector<StringRef, 20> elements; + str.split(elements, ';'); + + StringMap<StringRef> pairs; + for (StringRef s : elements) { + std::pair<StringRef, StringRef> pair = s.split(':'); + if (pairs.count(pair.first)) + return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first); + + pairs.insert(s.split(':')); + } + + return pairs; +} +} // namespace llgs_tests Index: unittests/tools/lldb-server/tests/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/tools/lldb-server/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +add_lldb_unittest(LLDBServerTests + TestClient.cpp + MessageObjects.cpp + ThreadIdsInJstopinfoTest.cpp + + LINK_LIBS + lldbHost + lldbCore + lldbInterpreter + lldbTarget + lldbPluginPlatformLinux + lldbPluginProcessGDBRemote + LINK_COMPONENTS + Support + ) Index: unittests/tools/lldb-server/inferior/thread_inferior.cpp =================================================================== --- /dev/null +++ unittests/tools/lldb-server/inferior/thread_inferior.cpp @@ -0,0 +1,39 @@ +//===-- thread_inferior.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <atomic> +#include <chrono> +#include <string> +#include <thread> +#include <vector> +#include "llvm/Support/Compiler.h" + +int main(int argc, char* argv[]) { + int thread_count = 2; + if (argc > 1) { + thread_count = std::stoi(argv[1], nullptr, 10); + } + + std::atomic<bool> delay(true); + std::vector<std::thread> threads; + for (int i = 0; i < thread_count; i++) { + threads.push_back(std::thread([&delay] { + while (delay.load()) + std::this_thread::sleep_for(std::chrono::seconds(1)); + })); + } + + LLVM_BUILTIN_DEBUGTRAP; + delay.store(false); + for (std::thread& t : threads) { + t.join(); + } + + return 0; +} Index: unittests/tools/lldb-server/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/tools/lldb-server/CMakeLists.txt @@ -0,0 +1,13 @@ +function(add_lldb_test_executable test_name) + set(EXCLUDE_FROM_ALL ON) + add_llvm_executable(${test_name} NO_INSTALL_RPATH ${ARGN}) + set(outdir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}) + set_output_directory(${test_name} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir}) +endfunction() + +add_lldb_test_executable(thread_inferior inferior/thread_inferior.cpp) + +add_definitions(-DLLDB_SERVER="$<TARGET_FILE:lldb-server>") +add_definitions(-DTHREAD_INFERIOR="${CMAKE_CURRENT_BINARY_DIR}/thread_inferior") +add_subdirectory(tests) +add_dependencies(LLDBServerTests thread_inferior) Index: unittests/tools/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(lldb-server) Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -68,9 +68,10 @@ add_subdirectory(Symbol) add_subdirectory(SymbolFile) add_subdirectory(Target) +add_subdirectory(tools) add_subdirectory(UnwindAssembly) add_subdirectory(Utility) if(LLDB_CAN_USE_DEBUGSERVER) add_subdirectory(debugserver) -endif() \ No newline at end of file +endif()
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits