jmajors updated this revision to Diff 98869.
jmajors marked 2 inline comments as done.
jmajors added a comment.
Changed shared_ptr<Type> returns to Expected<unique_ptr<Type>>.
Some other error processing additions.
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,50 @@
+//===-- 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 <string>
+
+#include "gtest/gtest.h"
+#include "TestClient.h"
+
+using namespace CommunicationTests;
+
+TEST(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());
+ client.StartDebugger();
+ client.SetInferior(inferior_args);
+ client.ListThreadsInStopReply();
+ client.Continue();
+ unsigned int pc_reg = client.GetPcRegisterId();
+
+ auto jthreads_info = client.GetJThreadsInfo();
+ ASSERT_TRUE(jthreads_info.get()) << "Error reading JThreadInfo.";
+
+ 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.";
+ ASSERT_EQ(stop_reply_pcs[tid], thread_infos[tid].ReadRegister(pc_reg))
+ << "Mismatched PC for thread: " << tid;
+ }
+
+ client.StopDebugger();
+}
Index: unittests/tools/lldb-server/tests/TestClient.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClient.h
@@ -0,0 +1,57 @@
+//===-- 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 <memory>
+#include <string>
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
+
+#include "MessageObjects.h"
+
+namespace CommunicationTests {
+// 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:
+ TestClient(const std::string& test_name, const std::string& test_case_name);
+ virtual ~TestClient();
+ void StartDebugger();
+ void StopDebugger();
+ void SetInferior(llvm::ArrayRef<std::string> inferior_args);
+ void ListThreadsInStopReply();
+ void SetBreakpoint(unsigned long address);
+ void Continue(unsigned long thread_id = 0);
+ const ProcessInfo& GetProcessInfo();
+ std::unique_ptr<JThreadsInfo> GetJThreadsInfo();
+ const StopReply& GetLatestStopReply();
+ void SendMessage(const std::string& message);
+ void SendMessage(const std::string& message, std::string& response_string);
+ void SendMessage(const std::string& message, std::string& response_string,
+ PacketResult expected_result);
+ unsigned int GetPcRegisterId();
+
+private:
+ void 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);
+
+ std::unique_ptr<ProcessInfo> process_info;
+ std::unique_ptr<StopReply> stop_reply;
+ lldb_private::ProcessLaunchInfo server_process_info;
+ std::string test_name;
+ std::string test_case_name;
+ unsigned int pc_register;
+};
+}
Index: unittests/tools/lldb-server/tests/TestClient.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClient.cpp
@@ -0,0 +1,250 @@
+//===-- 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 <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <future>
+
+#include "gtest/gtest.h"
+#include "TestClient.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/HostInfo.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"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+namespace CommunicationTests {
+const char* LOCALHOST = "127.0.0.1";
+
+TestClient::TestClient(const std::string& test_name,
+ const std::string& test_case_name)
+: test_name(test_name), test_case_name(test_case_name), pc_register(UINT_MAX) {
+ HostInfo::Initialize();
+}
+
+TestClient::~TestClient() {
+}
+
+void TestClient::StartDebugger() {
+ const ArchSpec& arch_spec = HostInfo::GetArchitecture();
+ Args args;
+ args.AppendArgument(LLDB_SERVER);
+ args.AppendArgument("gdbserver");
+ args.AppendArgument("--log-file=" + GenerateLogFileName(arch_spec));
+ args.AppendArgument("--log-channels=gdb-remote packets");
+ args.AppendArgument("--reverse-connect");
+ std::string connectionAddress;
+ GenerateConnectionAddress(connectionAddress);
+
+ args.AppendArgument(connectionAddress);
+ server_process_info.SetArchitecture(arch_spec);
+ server_process_info.SetArguments(args, true);
+ Status status = Host::LaunchProcess(server_process_info);
+ // TODO: Move ASSERTS to test, return an Error.
+ ASSERT_FALSE(status.Fail())
+ << "Failure to launch lldb server: " << status.AsCString();
+
+ sleep(5); // TODO: Sleep is bad. Can I wait for it to start?
+ SendAck(); // Send this as a handshake.
+}
+
+void TestClient::StopDebugger() {
+ std::string response;
+ SendMessage("k", response, PacketResult::ErrorDisconnected);
+}
+
+void 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;
+ }
+
+ SendMessage(command.str());
+ SendMessage("qLaunchSuccess");
+ std::string response;
+ SendMessage("qProcessInfo", response);
+ auto create_or_error = ProcessInfo::Create(response);
+ if (auto create_error = create_or_error.takeError()) {
+ FAIL() << toString(std::move(create_error));
+ }
+
+ process_info = std::move(*create_or_error);
+}
+
+void TestClient::ListThreadsInStopReply() {
+ SendMessage("QListThreadsInStopReply");
+}
+
+void TestClient::SetBreakpoint(unsigned long address) {
+ std::stringstream command;
+ command << "Z0," << std::hex << address << ",1";
+ SendMessage(command.str());
+}
+
+void TestClient::Continue(unsigned long thread_id) {
+ // TODO!!!! Move ASSERTs to test. Return an Error
+ // ASSERT_TRUE(process_info.get())
+ // << "Continue() called before process_info initialized.";
+
+ if (thread_id == 0) {
+ thread_id = process_info->GetPid();
+ }
+
+ std::string message = formatv("vCont;c:{0:x-}", thread_id).str();
+ std::string response;
+ SendMessage(message, response);
+ auto creation = StopReply::Create(response, process_info->GetEndian());
+ if (auto create_error = creation.takeError()) {
+ FAIL() << toString(std::move(create_error));
+ }
+
+ stop_reply = std::move(*creation);
+}
+
+const ProcessInfo& TestClient::GetProcessInfo() {
+ return *(process_info.get());
+}
+
+std::unique_ptr<JThreadsInfo> TestClient::GetJThreadsInfo() {
+ std::string response;
+ SendMessage("jThreadsInfo", response);
+ auto creation = JThreadsInfo::Create(response, process_info->GetEndian());
+ // TODO: Move all ASSERT/FAIL calls out.
+ // Replace with returning an Error or Expected
+ if (auto create_error = creation.takeError()) {
+ // FAIL() << toString(std::move(create_error));
+ return std::unique_ptr<JThreadsInfo>(nullptr);
+ }
+
+ return std::move(*creation);
+}
+
+const StopReply& TestClient::GetLatestStopReply() {
+ return *(stop_reply.get());
+}
+
+void TestClient::SendMessage(const std::string& message) {
+ std::string dummy_string;
+ SendMessage(message, dummy_string);
+}
+
+void TestClient::SendMessage(const std::string& message,
+ std::string& response_string) {
+ SendMessage(message, response_string, PacketResult::Success);
+ // TODO: Move ASSERTS to test, return an Error.
+ ASSERT_NE(response_string[0], 'E')
+ << "Error " << response_string << " while sending message: " << message;
+}
+
+void TestClient::SendMessage(const std::string& message,
+ std::string& response_string,
+ PacketResult expected_result) {
+ StringExtractorGDBRemote response;
+ PacketResult result = SendPacketAndWaitForResponse(message, response, false);
+ response.GetEscapedBinaryData(response_string);
+ // TODO: Move ASSERTS to test, return an Error.
+ ASSERT_EQ(result, expected_result)
+ << FormatFailedResult(message, result);
+}
+
+unsigned int TestClient::GetPcRegisterId() {
+ if (pc_register != UINT_MAX) {
+ return pc_register;
+ }
+
+ for (unsigned int register_id = 0; ; register_id++) {
+ std::string message = formatv("qRegisterInfo{0:x-}", register_id).str();
+ // This will throw if we scan all registers and never get the
+ // expected result. Is that okay? Or is there a situation where
+ // no result is valid?
+ std::string response;
+ SendMessage(message, response);
+ auto elements = SplitPairList(response);
+ if (elements["alt-name"] == "pc" || elements["generic"] == "pc") {
+ pc_register = register_id;
+ break;
+ }
+ }
+
+ return pc_register;
+}
+
+void TestClient::GenerateConnectionAddress(std::string& address) {
+ StartListenThread(LOCALHOST, 0);
+ auto connection = (ConnectionFileDescriptor*)GetConnection();
+ uint16_t listening_port = connection->GetListeningPort(UINT32_MAX);
+ // TODO: Move ASSERTS to test, return an Error.
+ ASSERT_GT(listening_port, 0) << "GetListeningPort failed.";
+
+ raw_string_ostream address_stream(address);
+ address_stream << LOCALHOST << ":" << listening_port;
+ address_stream.str();
+}
+
+std::string TestClient::GenerateLogFileName(const ArchSpec& arch) const {
+ std::string log_file_name;
+ raw_string_ostream log_file(log_file_name);
+ log_file << "lldb-test-traces/lldb-" << test_case_name << '-' << test_name
+ << '-' << arch.GetArchitectureName() << ".log";
+ log_file.str();
+ return log_file_name;
+}
+
+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;
+}
+}
Index: unittests/tools/lldb-server/tests/MessageObjects.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/MessageObjects.h
@@ -0,0 +1,117 @@
+//===-- 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 <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+#include "lldb/lldb-types.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace CommunicationTests {
+ class ThreadInfo;
+ typedef llvm::DenseMap<unsigned long, ThreadInfo> ThreadInfoMap;
+ typedef llvm::DenseMap<unsigned long, unsigned long> ULongMap;
+
+class ParsingError : public llvm::ErrorInfo<ParsingError> {
+public:
+ static char ID;
+
+ ParsingError(llvm::StringRef source) : source(source) { }
+ const std::string& GetSource() { return source; }
+
+ void log(llvm::raw_ostream &OS) const override {
+ OS << source << " argument was invalid";
+ }
+
+ std::error_code convertToErrorCode() const override {
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+
+private:
+ std::string source;
+};
+
+class ProcessInfo {
+public:
+ static llvm::Expected<std::unique_ptr<ProcessInfo>> Create(llvm::StringRef response);
+ pid_t GetPid() const;
+ llvm::support::endianness GetEndian() const;
+
+protected:
+ ProcessInfo();
+
+private:
+ pid_t pid;
+ pid_t parent_pid;
+ uid_t real_uid;
+ gid_t real_gid;
+ uid_t effective_uid;
+ gid_t effective_gid;
+ std::string triple;
+ llvm::SmallString<16> ostype;
+ llvm::support::endianness endian;
+ unsigned int ptrsize;
+};
+
+class ThreadInfo {
+public:
+ ThreadInfo();
+ explicit ThreadInfo(llvm::StringRef name, llvm::StringRef reason,
+ const ULongMap& registers, unsigned int signal);
+
+ unsigned long ReadRegister(unsigned long register_id) const;
+
+private:
+ llvm::StringRef name;
+ llvm::StringRef reason;
+ ULongMap registers;
+ unsigned int signal;
+};
+
+class JThreadsInfo {
+public:
+ static llvm::Expected<std::unique_ptr<JThreadsInfo>>
+ Create(llvm::StringRef response, llvm::support::endianness endian);
+
+ const ThreadInfoMap& GetThreadInfos() const;
+
+protected:
+ JThreadsInfo();
+
+private:
+ ThreadInfoMap thread_infos;
+};
+
+class StopReply {
+public:
+ static llvm::Expected<std::unique_ptr<StopReply>>
+ Create(llvm::StringRef response, llvm::support::endianness endian);
+ const ULongMap& GetThreadPcs() const;
+
+protected:
+ StopReply();
+
+private:
+ void ParseResponse(llvm::StringRef response,
+ llvm::support::endianness endian);
+ unsigned int signal;
+ lldb::tid_t thread;
+ llvm::StringRef name;
+ ULongMap thread_pcs;
+ ULongMap registers;
+ llvm::StringRef reason;
+};
+
+// Common functions for parsing packet data.
+llvm::StringMap<llvm::StringRef> SplitPairList(llvm::StringRef s);
+}
Index: unittests/tools/lldb-server/tests/MessageObjects.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/MessageObjects.cpp
@@ -0,0 +1,246 @@
+//===-- 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 <iomanip>
+#include <sstream>
+
+#include "lldb/Core/StructuredData.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include "gtest/gtest.h"
+#include "MessageObjects.h"
+
+using namespace lldb_private;
+using namespace llvm;
+using namespace llvm::support;
+namespace CommunicationTests {
+char ParsingError::ID = 1;
+
+Expected<std::unique_ptr<ProcessInfo>> ProcessInfo::Create(StringRef response) {
+ std::unique_ptr<ProcessInfo> process_info =
+ std::unique_ptr<ProcessInfo>(new ProcessInfo);
+ auto elements = SplitPairList(response);
+ if (elements["pid"].getAsInteger(16, process_info->pid)) {
+ return make_error<ParsingError>("ProcessInfo: pid");
+ }
+ if (elements["parent-pid"].getAsInteger(16, process_info->parent_pid)) {
+ return make_error<ParsingError>("ProcessInfo: parent-pid");
+ }
+ if (elements["real-uid"].getAsInteger(16, process_info->real_uid)) {
+ return make_error<ParsingError>("ProcessInfo: real-uid");
+ }
+ if (elements["real-gid"].getAsInteger(16, process_info->real_gid)) {
+ return make_error<ParsingError>("ProcessInfo: real-uid");
+ }
+ if (elements["effective-uid"].getAsInteger(16, process_info->effective_uid)) {
+ return make_error<ParsingError>("ProcessInfo: effective-uid");
+ }
+ if (elements["effective-gid"].getAsInteger(16, process_info->effective_gid)) {
+ return make_error<ParsingError>("ProcessInfo: effective-gid");
+ }
+ if (elements["ptrsize"].getAsInteger(10, process_info->ptrsize)) {
+ return make_error<ParsingError>("ProcessInfo: ptrsize");
+ }
+
+ process_info->triple = fromHex(elements["triple"]);
+ process_info->endian = llvm::StringSwitch<endianness>(elements["endian"])
+ .Case("little", support::little)
+ .Case("big", support::big)
+ .Default(support::native);
+
+ return std::move(process_info);
+}
+
+pid_t ProcessInfo::GetPid() const {
+ return pid;
+}
+
+endianness ProcessInfo::GetEndian() const {
+ return endian;
+}
+
+ProcessInfo::ProcessInfo() {
+}
+
+//====== ThreadInfo ============================================================
+ThreadInfo::ThreadInfo() {
+ // For map [] operators.
+}
+
+ThreadInfo::ThreadInfo(StringRef name, StringRef reason,
+ const ULongMap& registers, unsigned int signal) :
+ name(name), reason(reason), registers(registers), signal(signal) {
+}
+
+unsigned long ThreadInfo::ReadRegister(unsigned long register_id) const {
+ return registers.lookup(register_id);
+}
+
+//====== JThreadsInfo ==========================================================
+Expected<std::unique_ptr<JThreadsInfo>>
+JThreadsInfo::Create(StringRef response, endianness endian) {
+ auto jthreads_info = std::unique_ptr<JThreadsInfo>(new JThreadsInfo);
+
+ StructuredData::ObjectSP json = StructuredData::ParseJSON(response);
+ StructuredData::Array* array = json->GetAsArray();
+ if (!array) {
+ return make_error<ParsingError>("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_error<ParsingError>(formatv("JThreadsInfo: JSON obj at {0}",
+ i).str());
+ }
+
+ StringRef name, reason;
+ thread_info->GetValueForKeyAsString("name", name);
+ thread_info->GetValueForKeyAsString("reason", reason);
+ unsigned long signal;
+ thread_info->GetValueForKeyAsInteger("signal", signal);
+ unsigned long tid;
+ thread_info->GetValueForKeyAsInteger("tid", tid);
+
+ StructuredData::Dictionary* register_dict;
+ thread_info->GetValueForKeyAsDictionary("registers", register_dict);
+ if (!register_dict) {
+ return make_error<ParsingError>("JThreadsInfo: registers JSON obj");
+ }
+
+ ULongMap 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;
+ unsigned long register_value;
+ if (key_str.getAsInteger(10, register_id)) {
+ return make_error<ParsingError>(
+ formatv("JThreadsInfo: register key[{0}]", i).str());
+ }
+ if (value_str.getAsInteger(16, register_value)) {
+ return make_error<ParsingError>(
+ formatv("JThreadsInfo: register value[{0}]", i).str());
+ }
+ if (endian == little) {
+ sys::swapByteOrder(register_value);
+ }
+
+ registers[register_id] = register_value;
+ }
+
+ jthreads_info->thread_infos[tid] =
+ ThreadInfo(name, reason, registers, signal);
+ }
+
+ return std::move(jthreads_info);
+}
+
+const ThreadInfoMap& JThreadsInfo::GetThreadInfos() const {
+ return thread_infos;
+}
+
+JThreadsInfo::JThreadsInfo() {
+}
+
+//====== StopReply =============================================================
+StopReply::StopReply() {
+}
+
+const ULongMap& StopReply::GetThreadPcs() const {
+ return thread_pcs;
+}
+
+Expected<std::unique_ptr<StopReply>>
+StopReply::Create(StringRef response, llvm::support::endianness endian) {
+ auto stop_reply = std::unique_ptr<StopReply>(new StopReply);
+
+ auto elements = SplitPairList(response);
+ stop_reply->name = elements["name"];
+ stop_reply->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_error<ParsingError>("StopReply: thread/PC count mismatch");
+ }
+
+ for (unsigned int i = 0; i < threads.size(); i++) {
+ lldb::tid_t thread_id;
+ unsigned long pc;
+ if (threads[i].getAsInteger(16, thread_id)) {
+ return make_error<ParsingError>(
+ formatv("StopReply: thread ID at [{0}].", i).str());
+ }
+ if (pcs[i].getAsInteger(16, pc)) {
+ return make_error<ParsingError>(
+ formatv("StopReply: thread PC at [{0}].", i).str());
+ }
+
+ stop_reply->thread_pcs[thread_id] = pc;
+ }
+
+ unsigned int register_id = 0;
+ while (true) {
+ std::stringstream ss;
+ ss << std::hex << std::setw(2) << std::setfill('0') << register_id;
+ std::string hex_id = ss.str();
+ if (elements.find(hex_id) != elements.end()) {
+ unsigned long register_value;
+ if (elements[hex_id].getAsInteger(16, register_value)) {
+ return make_error<ParsingError>(
+ formatv("StopReply: Register value at [{0}].", register_id).str());
+ }
+ if (endian == little) {
+ sys::swapByteOrder(register_value);
+ }
+
+ stop_reply->registers[register_id++] = register_value;
+ }
+ else {
+ break;
+ }
+ }
+
+ 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->thread)) {
+ return make_error<ParsingError>("StopReply: thread id");
+ }
+ if (key.substr(1, 2).getAsInteger(16, stop_reply->signal)) {
+ return make_error<ParsingError>("StopReply: stop signal");
+ }
+ }
+ }
+
+ return std::move(stop_reply);
+}
+
+//====== Globals ===============================================================
+StringMap<StringRef> SplitPairList(StringRef str) {
+ SmallVector<StringRef, 20> elements;
+ str.split(elements, ';');
+
+ StringMap<StringRef> pairs;
+ for (StringRef s : elements) {
+ pairs.insert(s.split(':'));
+ }
+
+ return pairs;
+}
+}
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,36 @@
+//===-- 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 <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);
+ }
+
+ bool delay = true;
+ std::vector<std::thread> threads;
+ for (int i = 0; i < thread_count; i++) {
+ threads.push_back(std::thread([&delay]{while(delay);}));
+ }
+
+ LLVM_BUILTIN_DEBUGTRAP;
+ delay = 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits