jmajors updated this revision to Diff 99491.
jmajors marked an inline comment as done.
jmajors added a comment.

More CL feedback.


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,55 @@
+//===-- 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 "TestClient.h"
+#include "gtest/gtest.h"
+
+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.Continue());
+  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.";
+    ASSERT_EQ(stop_reply_pcs[tid], thread_infos[tid].ReadRegister(pc_reg))
+        << "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,58 @@
+//===-- 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 "MessageObjects.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+
+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 Continue(unsigned long thread_id = 0);
+  const ProcessInfo& GetProcessInfo();
+  std::unique_ptr<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 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;
+};
+}  // namespace llgs_tests
Index: unittests/tools/lldb-server/tests/TestClient.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClient.cpp
@@ -0,0 +1,266 @@
+//===-- 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 <cstdlib>
+#include <future>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "gtest/gtest.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"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+namespace llgs_tests {
+static const char g_local_host[] = "127.0.0.1";
+
+void TestClient::Initialize() { HostInfo::Initialize(); }
+
+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) {}
+
+TestClient::~TestClient() {}
+
+bool 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;
+  if (!GenerateConnectionAddress(connectionAddress)) return false;
+
+  args.AppendArgument(connectionAddress);
+  server_process_info.SetArchitecture(arch_spec);
+  server_process_info.SetArguments(args, true);
+  Status status = Host::LaunchProcess(server_process_info);
+  if (status.Fail()) {
+    GTEST_LOG_(ERROR) << formatv("Failure to launch lldb server: {0}.",
+                                 status.AsCString()).str();
+    return false;
+  }
+
+  sleep(5);   // TODO: Sleep is bad. Can I wait for it to start?
+  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;
+  }
+
+  process_info = std::move(*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::Continue(unsigned long thread_id) {
+  if (!process_info) {
+    GTEST_LOG_(ERROR) << "Continue() called before process_info initialized.";
+    return false;
+  }
+
+  std::string message;
+  if (thread_id == 0)
+    // Continue all threads.
+    message = "vCont;c";
+  else
+    message = formatv("vCont;c:{0:x-}", thread_id).str();
+
+  std::string response;
+  if (!SendMessage(message, response)) return false;
+  auto creation = StopReply::Create(response, process_info->GetEndian());
+  if (auto create_error = creation.takeError()) {
+    GTEST_LOG_(ERROR) << toString(std::move(create_error));
+    return false;
+  }
+
+  stop_reply = std::move(*creation);
+  return true;
+}
+
+const ProcessInfo& TestClient::GetProcessInfo() { return *process_info; }
+
+std::unique_ptr<JThreadsInfo> TestClient::GetJThreadsInfo() {
+  std::string response;
+  if (!SendMessage("jThreadsInfo", response)) return nullptr;
+  auto creation = JThreadsInfo::Create(response, process_info->GetEndian());
+  if (auto create_error = creation.takeError()) {
+    GTEST_LOG_(ERROR) << toString(std::move(create_error));
+    return nullptr;
+  }
+
+  return std::move(*creation);
+}
+
+const StopReply& TestClient::GetLatestStopReply() {
+  return *(stop_reply.get());
+}
+
+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 (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();
+    std::string response;
+    if (!SendMessage(message, response)) {
+      GTEST_LOG_(ERROR) << "Unable to query register ID for PC register.";
+      return UINT_MAX;
+    }
+
+    auto elements = SplitPairList(response);
+    if (elements["alt-name"] == "pc" || elements["generic"] == "pc") {
+      pc_register = register_id;
+      break;
+    }
+  }
+
+  return pc_register;
+}
+
+bool TestClient::GenerateConnectionAddress(std::string& address) {
+  StartListenThread(g_local_host, 0);
+  auto connection = (ConnectionFileDescriptor*)GetConnection();
+  uint16_t listening_port = connection->GetListeningPort(UINT32_MAX);
+  if (listening_port <= 0) {
+    GTEST_LOG_(ERROR) << "GetListeningPort failed.";
+    return false;
+  }
+
+  raw_string_ostream address_stream(address);
+  address_stream << g_local_host << ":" << listening_port;
+  address_stream.str();
+  return true;
+}
+
+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";
+  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,94 @@
+//===-- 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 "lldb/lldb-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+
+namespace llgs_tests {
+class ThreadInfo;
+typedef llvm::DenseMap<uint64_t, ThreadInfo> ThreadInfoMap;
+typedef llvm::DenseMap<uint64_t, uint64_t> U64Map;
+
+class ProcessInfo {
+ public:
+  static llvm::Expected<std::unique_ptr<ProcessInfo>> Create(
+      llvm::StringRef response);
+  lldb::pid_t GetPid() const;
+  llvm::support::endianness GetEndian() const;
+
+ private:
+  ProcessInfo();
+  lldb::pid_t pid;
+  lldb::pid_t parent_pid;
+  uint32_t real_uid;
+  uint32_t real_gid;
+  uint32_t effective_uid;
+  uint32_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 U64Map& registers, unsigned int signal);
+
+  uint64_t ReadRegister(uint64_t register_id) const;
+
+ private:
+  std::string name;
+  std::string reason;
+  U64Map 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;
+
+ private:
+  JThreadsInfo();
+  ThreadInfoMap thread_infos;
+};
+
+class StopReply {
+ public:
+  static llvm::Expected<std::unique_ptr<StopReply>> Create(
+      llvm::StringRef response, llvm::support::endianness endian);
+  const U64Map& GetThreadPcs() const;
+
+ private:
+  StopReply();
+  void ParseResponse(llvm::StringRef response,
+                     llvm::support::endianness endian);
+  unsigned int signal;
+  lldb::tid_t thread;
+  std::string name;
+  U64Map thread_pcs;
+  U64Map registers;
+  std::string reason;
+};
+
+// Common functions for parsing packet data.
+llvm::Error make_parsing_error(const char *format, uint64_t number);
+llvm::Error make_parsing_error(llvm::StringRef parse_target);
+llvm::StringMap<llvm::StringRef> SplitPairList(llvm::StringRef s);
+}  // namespace llgs_tests
Index: unittests/tools/lldb-server/tests/MessageObjects.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/MessageObjects.cpp
@@ -0,0 +1,202 @@
+//===-- 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 "gtest/gtest.h"
+#include "lldb/Core/StructuredData.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb_private;
+using namespace llvm;
+using namespace llvm::support;
+namespace llgs_tests {
+
+Expected<std::unique_ptr<ProcessInfo>> ProcessInfo::Create(StringRef response) {
+  std::unique_ptr<ProcessInfo> process_info(new ProcessInfo);
+  auto elements = SplitPairList(response);
+  if (elements["pid"].getAsInteger(16, process_info->pid))
+    return make_parsing_error("ProcessInfo: pid");
+  if (elements["parent-pid"].getAsInteger(16, process_info->parent_pid))
+    return make_parsing_error("ProcessInfo: parent-pid");
+  if (elements["real-uid"].getAsInteger(16, process_info->real_uid))
+    return make_parsing_error("ProcessInfo: real-uid");
+  if (elements["real-gid"].getAsInteger(16, process_info->real_gid))
+    return make_parsing_error("ProcessInfo: real-uid");
+  if (elements["effective-uid"].getAsInteger(16, process_info->effective_uid))
+    return make_parsing_error("ProcessInfo: effective-uid");
+  if (elements["effective-gid"].getAsInteger(16, process_info->effective_gid))
+    return make_parsing_error("ProcessInfo: effective-gid");
+  if (elements["ptrsize"].getAsInteger(10, process_info->ptrsize))
+    return make_parsing_error("ProcessInfo: ptrsize");
+
+  process_info->triple = fromHex(elements["triple"]);
+  StringRef endian_str = elements["endian"];
+  if (endian_str == "little")
+    process_info->endian = support::little;
+  else if (endian_str == "big")
+    process_info->endian = support::big;
+  else
+    return make_parsing_error("ProcessInfo: endian");
+
+  return std::move(process_info);
+}
+
+lldb::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 U64Map& registers, unsigned int signal)
+    : name(name.str()), reason(reason.str()), registers(registers),
+    signal(signal) {}
+
+uint64_t ThreadInfo::ReadRegister(uint64_t register_id) const {
+  return registers.lookup(register_id);
+}
+
+//====== JThreadsInfo ==========================================================
+Expected<std::unique_ptr<JThreadsInfo>> JThreadsInfo::Create(
+    StringRef response, endianness endian) {
+  std::unique_ptr<JThreadsInfo> jthreads_info(new JThreadsInfo);
+
+  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");
+
+    U64Map 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;
+      uint64_t register_value;
+      if (key_str.getAsInteger(10, register_id))
+        return make_parsing_error("JThreadsInfo: register key[{0}]", i);
+      if (value_str.getAsInteger(16, register_value))
+        return make_parsing_error("JThreadsInfo: register value[{0}]", i);
+      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 U64Map& StopReply::GetThreadPcs() const { return thread_pcs; }
+
+Expected<std::unique_ptr<StopReply>> StopReply::Create(
+    StringRef response, llvm::support::endianness endian) {
+  std::unique_ptr<StopReply> stop_reply(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_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->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->thread))
+        return make_parsing_error("StopReply: thread id");
+      if (key.substr(1, 2).getAsInteger(16, stop_reply->signal))
+        return make_parsing_error("StopReply: stop signal");
+    } else if (key.size() == 2) {
+      unsigned int reg;
+      if (!key.getAsInteger(16, reg)) {
+        uint64_t register_value;
+        if (val.getAsInteger(16, register_value))
+          return make_parsing_error("StopReply: Register value at [{0}].", reg);
+        if (endian == little) sys::swapByteOrder(register_value);
+      }
+    }
+  }
+
+  return std::move(stop_reply);
+}
+
+//====== Globals ===============================================================
+Error make_parsing_error(const char *format, uint64_t number) {
+  return make_parsing_error(formatv(format, number).str());
+}
+
+Error make_parsing_error(StringRef parse_target) {
+  std::string error = formatv("Unable to parse {0}", parse_target).str();
+  return make_error<StringError>(error, inconvertibleErrorCode());
+}
+
+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;
+}
+}  // 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,37 @@
+//===-- 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 <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);
+  }
+
+  bool delay = true;
+  std::vector<std::thread> threads;
+  for (int i = 0; i < thread_count; i++) {
+    threads.push_back(std::thread([&delay] {
+      while (delay) std::this_thread::sleep_for(std::chrono::seconds(1));
+    }));
+  }
+
+  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
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to