jmajors created this revision.
Herald added a subscriber: mgorny.

This is a new C++ test framework based on Google Test, and one working
example test.
The intention is to replace the existing tests in
packages/Python/lldbsuite/test/tools/lldb-server/ with this suite
and use this framework for all new client server tests.


https://reviews.llvm.org/D32930

Files:
  unittests/CMakeLists.txt
  unittests/tools/CMakeLists.txt
  unittests/tools/lldb-server/.gitignore
  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/TestClientException.cpp
  unittests/tools/lldb-server/tests/TestClientException.h
  unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp
  unittests/tools/lldb-server/tests/gtest_common.h

Index: unittests/tools/lldb-server/tests/gtest_common.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/gtest_common.h
@@ -0,0 +1,26 @@
+//===-- gtest_common.h ------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(LLDB_GTEST_COMMON_H)
+#error "gtest_common.h should not be included manually."
+#else
+#define LLDB_GTEST_COMMON_H
+#endif
+
+// This header file is force included by all of LLDB's unittest compilation
+// units.  Be very leary about putting anything in this file.
+
+#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
+// Due to a bug in <thread>, when _HAS_EXCEPTIONS == 0 the header will try to
+// call
+// uncaught_exception() without having a declaration for it.  The fix for this
+// is
+// to manually #include <eh.h>, which contains this declaration.
+#include <eh.h>
+#endif
Index: unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp
@@ -0,0 +1,52 @@
+//===-- 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 <vector>
+
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "TestClient.h"
+
+using namespace std;
+using namespace CommunicationTests;
+
+TEST(ThreadsInJstopinfoTest, TestStopReplyContainsThreadPcsLlgs) {
+  vector<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();
+
+  JThreadsInfo jthreads_info = client.GetJThreadsInfo();
+  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/TestClientException.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClientException.h
@@ -0,0 +1,21 @@
+//===-- 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 <exception>
+#include <string>
+
+namespace CommunicationTests {
+class TestClientException : public std::exception {
+public:
+  TestClientException(const std::string& message);
+  const char* what() const noexcept;
+private:
+  std::string message;
+};
+}
Index: unittests/tools/lldb-server/tests/TestClientException.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClientException.cpp
@@ -0,0 +1,13 @@
+#include "TestClientException.h"
+
+using namespace std;
+
+namespace CommunicationTests {
+TestClientException::TestClientException(const string& message) :
+  message(message) {
+}
+
+const char* TestClientException::what() const noexcept {
+  return message.c_str();
+}
+}
Index: unittests/tools/lldb-server/tests/TestClient.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/TestClient.h
@@ -0,0 +1,56 @@
+//===-- 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(const std::vector<std::string>& inferior_args);
+  void ListThreadsInStopReply();
+  void SetBreakpoint(unsigned long address);
+  void Continue(unsigned long thread_id = 0);
+  void StepThread(unsigned long thread_id, int count = 1);
+  std::shared_ptr<ProcessInfo> GetProcessInfo();
+  JThreadsInfo GetJThreadsInfo();
+  std::shared_ptr<StopReply> GetLatestStopReply();
+  void SendMessage(const std::string& message);
+  void SendMessage(const std::string& message, std::string& response_string);
+  unsigned int GetPcRegisterId();
+
+private:
+  void GenerateConnectionAddress(std::string& address);
+  std::string GenerateLogFileName(const lldb_private::ArchSpec& arch) const;
+  std::string HexEncode(const std::string& s) const;
+  std::string FormatFailedResult(const std::string& message,
+      lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult result);
+
+  std::shared_ptr<ProcessInfo> process_info;
+  std::shared_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,239 @@
+//===-- 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include "gtest/gtest.h"
+#include "TestClient.h"
+#include "TestClientException.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"
+
+using namespace std;
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+namespace CommunicationTests {
+const char* LOCALHOST = "127.0.0.1";
+
+TestClient::TestClient(const string& test_name, const 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.SetCommandString(LLDB_SERVER);
+  args.AppendArgument("gdbserver");
+  args.AppendArgument("--log-file=" + GenerateLogFileName(arch_spec));
+  args.AppendArgument("--log-channels=gdb-remote packets");
+  args.AppendArgument("--reverse-connect");
+  string connectionAddress;
+  GenerateConnectionAddress(connectionAddress);
+
+  args.AppendArgument(connectionAddress);
+  server_process_info.SetArchitecture(arch_spec);
+  server_process_info.SetArguments(args, true);
+  Error error = Host::LaunchProcess(server_process_info);
+  ASSERT_FALSE(error.Fail())
+    << "Failure to launch lldb server: " << error.AsCString();
+
+  sleep(5); // TODO: Sleep is bad. Can I wait for it to start?
+  SendAck(); // Send this as a handshake.
+}
+
+void TestClient::StopDebugger() {
+  Host::Kill(server_process_info.GetProcessID(), 15);
+}
+
+void TestClient::SetInferior(const vector<string>& inferior_args) {
+  stringstream command;
+  command << "A";
+  for (size_t i = 0; i < inferior_args.size(); i++) {
+    if (i > 0) command << ',';
+    string hex_encoded = HexEncode(inferior_args[i]);
+    command << hex_encoded.size() << ',' << i << ',' << hex_encoded;
+  }
+  
+  SendMessage(command.str());
+  SendMessage("qLaunchSuccess");
+  string response;
+  SendMessage("qProcessInfo", response);
+  process_info.reset(new ProcessInfo(response));
+}
+
+void TestClient::ListThreadsInStopReply() {
+  SendMessage("QListThreadsInStopReply");
+}
+
+void TestClient::SetBreakpoint(unsigned long address) {
+  stringstream command;
+  command << "Z0," << hex << address << ",1";
+  SendMessage(command.str());
+}
+
+void TestClient::Continue(unsigned long thread_id) {
+  if (thread_id == 0) {
+    thread_id = process_info->GetPid();
+  }
+
+  stringstream message;
+  message << "vCont;c:" << hex << thread_id;
+  string response;
+  SendMessage(message.str(), response);
+  stop_reply.reset(new StopReply(response));
+}
+
+void TestClient::StepThread(unsigned long thread_id, int count) {
+  if (thread_id == 0) {
+    thread_id = process_info->GetPid();
+  }
+
+  stringstream message;
+  message << "vCont;s:" << hex << thread_id;
+  string response;
+  for (int i = 0; i < count; i++) {
+    SendMessage(message.str(), response);
+  }
+
+  stop_reply.reset(new StopReply(response));
+}
+
+shared_ptr<ProcessInfo> TestClient::GetProcessInfo() {
+  return process_info;
+}
+
+JThreadsInfo TestClient::GetJThreadsInfo() {
+  string response;
+  SendMessage("jThreadsInfo", response);
+  return JThreadsInfo(response, process_info->GetEndian());
+}
+
+shared_ptr<StopReply> TestClient::GetLatestStopReply() {
+  return stop_reply;
+}
+
+void TestClient::SendMessage(const string& message) {
+  string dummy_string;
+  SendMessage(message, dummy_string);
+}
+
+void TestClient::SendMessage(const string& message, string& response_string) {
+  StringExtractorGDBRemote response;
+  PacketResult result = SendPacketAndWaitForResponse(message, response, false);
+  ASSERT_EQ(result, PacketResult::Success)
+    << FormatFailedResult(message, result);
+
+  response.GetEscapedBinaryData(response_string);
+  ASSERT_NE(response_string[0], 'E')
+    << "Error " << response_string << " while sending message: " << message;
+}
+
+unsigned int TestClient::GetPcRegisterId() {
+  if (pc_register == UINT_MAX) {
+    for (unsigned int register_id = 0; ; register_id++) {
+      stringstream message;
+      message << "qRegisterInfo" << hex << register_id;
+      // 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?
+      string response;
+      SendMessage(message.str(), response);
+      auto elements = SplitPairList(response);
+      if (elements["alt-name"] == "pc" || elements["generic"] == "pc") {
+        pc_register = register_id;
+        break;
+      }
+    }
+  }
+
+  return pc_register;
+}
+
+void TestClient::GenerateConnectionAddress(string& address) {
+  StartListenThread(LOCALHOST, 0);
+  auto connection = (ConnectionFileDescriptor*)GetConnection();
+  uint16_t listening_port = connection->GetListeningPort(UINT32_MAX);
+  ASSERT_GT(listening_port, 0) << "GetListeningPort failed.";
+
+  stringstream ss;
+  ss << LOCALHOST << ":" << listening_port << ends;
+  address = ss.str();
+}
+
+string TestClient::GenerateLogFileName(const ArchSpec& arch) const {
+  stringstream log_file;
+  log_file << "lldb-test-traces/lldb-" << test_case_name << '-' << test_name
+           << '-' << arch.GetArchitectureName() << ".log";
+  return log_file.str();
+}
+
+string TestClient::HexEncode(const string& s) const {
+  stringstream encoded;
+  for (const char& c : s) {
+    encoded << hex << (int)c;
+  }
+
+  return encoded.str();
+}
+string TestClient::FormatFailedResult(const string& message,
+                                      PacketResult result) {
+  stringstream ss;
+  ss << "Failure sending message: " << message << " Result: ";
+    
+  switch (result) {
+  case PacketResult::ErrorSendFailed:
+    ss << "ErrorSendFailed";
+    break;
+  case PacketResult::ErrorSendAck:
+    ss << "ErrorSendAck";
+    break;
+  case PacketResult::ErrorReplyFailed:
+    ss << "ErrorReplyFailed";
+    break;
+  case PacketResult::ErrorReplyTimeout:
+    ss << "ErrorReplyTimeout";
+    break;
+  case PacketResult::ErrorReplyInvalid:
+    ss << "ErrorReplyInvalid";
+    break;
+  case PacketResult::ErrorReplyAck:
+    ss << "ErrorReplyAck";
+    break;
+  case PacketResult::ErrorDisconnected:
+    ss << "ErrorDisconnected";
+    break;
+  case PacketResult::ErrorNoSequenceLock:
+    ss << "ErrorNoSequenceLock";
+    break;
+  default:
+    ss << "Unknown Error";
+  }
+
+  return ss.str();
+}
+}
Index: unittests/tools/lldb-server/tests/MessageObjects.h
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/MessageObjects.h
@@ -0,0 +1,88 @@
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace CommunicationTests {
+  class ThreadInfo;
+  typedef std::unordered_map<unsigned long, ThreadInfo> ThreadInfoMap;
+  typedef std::unordered_map<unsigned long, unsigned long> ULongMap;
+
+enum Endian {
+  LITTLE,
+  BIG,
+  UNKNOWN
+};
+
+class ProcessInfo {
+public:
+  ProcessInfo(const std::string& response);
+  unsigned int GetPid() const;
+  Endian GetEndian() const;
+
+private:
+  unsigned int pid;
+  unsigned int parent_pid;
+  unsigned int real_uid;
+  unsigned int real_gid;
+  unsigned int effective_uid;
+  unsigned int effective_gid;
+  std::string triple;
+  std::string ostype;
+  Endian endian;
+  unsigned int ptrsize;
+};
+
+class ThreadInfo {
+public:
+  ThreadInfo();
+  ThreadInfo(const std::string& name, const std::string& reason,
+             const ULongMap& registers, unsigned int signal);
+
+  unsigned long ReadRegister(unsigned long register_id) const;
+
+private:
+  std::string name;
+  std::string reason;
+  ULongMap registers;
+  unsigned int signal;
+};
+
+class JThreadsInfo {
+public:
+  JThreadsInfo(const std::string& response, Endian endian);
+
+  const ThreadInfoMap& GetThreadInfos() const;
+
+private:
+  ThreadInfoMap thread_infos;
+};
+
+class JStopInfo {
+public:
+  JStopInfo(const std::string& response);
+};
+
+class StopReply {
+public:
+  StopReply(const std::string& response);
+
+  const ULongMap& GetThreadPcs() const;
+
+private:
+  unsigned int signal;
+  unsigned long thread;
+  std::string name;
+  std::shared_ptr<JStopInfo> jstopinfo;
+  ULongMap thread_pcs;
+  ULongMap registers;
+  std::string reason;
+};
+
+// Common functions for parsing packet data.
+std::unordered_map<std::string, std::string> SplitPairList(const std::string& s);
+std::vector<std::string> SplitList(const std::string& s, char delimeter);
+std::pair<std::string, std::string> SplitPair(const std::string& s);
+std::string HexDecode(const std::string& hex_encoded);
+unsigned long SwitchEndian(const std::string& little_endian);
+}
Index: unittests/tools/lldb-server/tests/MessageObjects.cpp
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/MessageObjects.cpp
@@ -0,0 +1,207 @@
+#include <iomanip>
+#include <sstream>
+
+#include "lldb/Core/StructuredData.h"
+
+#include "MessageObjects.h"
+#include "TestClientException.h"
+
+using namespace std;
+using namespace lldb_private;
+using namespace llvm;
+namespace CommunicationTests {
+ProcessInfo::ProcessInfo(const string& response) {
+  auto elements = SplitPairList(response);
+  pid = stoi(elements["pid"], nullptr, 16);
+  parent_pid = stoi(elements["parent-pid"], nullptr, 16);
+  real_uid = stoi(elements["real-uid"], nullptr, 16);
+  real_gid = stoi(elements["real-gid"], nullptr, 16);
+  effective_uid = stoi(elements["effective-uid"], nullptr, 16);
+  effective_gid = stoi(elements["effective-gid"], nullptr, 16);
+  triple = HexDecode(elements["triple"]);
+  if (elements["endian"] == "little") {
+    endian = LITTLE;
+  }
+  else if (elements["endian"] == "big") {
+    endian = BIG;
+  }
+  else {
+    endian = UNKNOWN;
+  }
+  
+  ptrsize = stoi(elements["ptrsize"], nullptr, 10);
+}
+
+unsigned int ProcessInfo::GetPid() const {
+  return pid;
+}
+
+Endian ProcessInfo::GetEndian() const {
+  return endian;
+}
+
+//====== ThreadInfo ============================================================
+ThreadInfo::ThreadInfo() { }
+ThreadInfo::ThreadInfo(const string& name, const string& 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.at(register_id);
+}
+
+//====== JThreadsInfo ==========================================================
+JThreadsInfo::JThreadsInfo(const string& response, Endian endian) {
+  StructuredData::ObjectSP json = StructuredData::ParseJSON(response);
+  StructuredData::Array* array = json->GetAsArray();
+  for (size_t i = 0; i < array->GetSize(); i++) {
+    StructuredData::Dictionary* thread_info;
+    array->GetItemAtIndexAsDictionary(i, thread_info);
+    string name;
+    thread_info->GetValueForKeyAsString("name", name);
+    string reason;
+    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);
+    ULongMap registers;
+
+    auto keys_obj = register_dict->GetKeys();
+    auto keys = keys_obj->GetAsArray();
+    for (size_t i = 0; i < keys->GetSize(); i++) {
+      string key_str;
+      keys->GetItemAtIndexAsString(i, key_str);
+      string value_str;
+      register_dict->GetValueForKeyAsString(key_str, value_str);
+      unsigned int register_id = stoul(key_str, nullptr, 10);
+      
+      if (endian == LITTLE) {
+        registers[register_id] = SwitchEndian(value_str);
+      }
+      else {
+        registers[register_id] = stoul(value_str, nullptr, 16);
+      }
+    }
+
+    thread_infos[tid] = ThreadInfo(name, reason, registers, signal);
+  }
+}
+
+const ThreadInfoMap& JThreadsInfo::GetThreadInfos() const {
+  return thread_infos;
+}
+
+//====== JStopInfo =============================================================
+JStopInfo::JStopInfo(const string& response) {
+  // TODO
+}
+
+//====== StopReply =============================================================
+StopReply::StopReply(const string& response) {
+  auto elements = SplitPairList(response);
+  name = elements["name"];
+  reason = elements["reason"];
+  vector<string> threads = SplitList(elements["threads"], ',');
+  // jstopinfo = JStopInfo(HexDecode(elements["jstopinfo"]));
+  vector<string> pcs = SplitList(elements["thread-pcs"], ',');
+  if (threads.size() != pcs.size()) {
+    throw TestClientException("Size mismatch between threads and thread-pcs.");
+  }
+
+  for (unsigned int i = 0; i < threads.size(); i++) {
+    thread_pcs[stol(threads[i], nullptr, 16)] = stol(pcs[i], nullptr, 16);
+  }
+
+  unsigned int register_id = 0;
+  while (true) {
+    stringstream ss;
+    ss << hex << setw(2) << setfill('0') << register_id;
+    string hex_id = ss.str();
+    if (elements.find(hex_id) != elements.end()) {
+      registers[register_id++] = SwitchEndian(elements[hex_id]);
+    }
+    else {
+      break;
+    }
+  }
+
+  for (auto i = elements.begin(); i != elements.end(); i++) {
+    if (i->first[0] == 'T' && i->first.substr(3, 6) == "thread") {
+      thread = stoul(i->second, nullptr, 16);
+      signal = stoul(i->first.substr(1, 2), nullptr, 16);
+    }
+  }
+}
+
+const ULongMap& StopReply::GetThreadPcs() const {
+  return thread_pcs;
+}
+
+//====== Globals ===============================================================
+unordered_map<string, string> SplitPairList(const string& s) {
+  unordered_map<string, string> pairs;
+  for (string& s : SplitList(s, ';')) {
+    pair<string, string> element = SplitPair(s);
+    pairs[element.first] = element.second;
+  }
+
+  return pairs;
+}
+
+vector<string> SplitList(const string& s, char delimeter) {
+  size_t start = 0;
+  vector<string> elements;
+  while (start < s.size()) {
+    size_t end = s.find_first_of(delimeter, start);
+    elements.push_back(s.substr(start, end - start));
+    if (end == string::npos) {
+      break;
+    }
+    else {
+      start = end + 1;
+    }
+  }
+
+  return elements;
+}
+
+pair<string, string> SplitPair(const string& s) {
+  pair<string, string> element;
+  size_t colon = s.find_first_of(':');
+  if (colon == string::npos) {
+    return element;
+  }
+
+  element.first = s.substr(0, colon);
+  element.second = s.substr(colon + 1);
+  return element;
+}
+
+string HexDecode(const string& hex_encoded) {
+  string decoded;
+  if (hex_encoded.size() % 2 == 1) {
+    return decoded;
+  }
+
+  for (size_t i = 0; i < hex_encoded.size(); i += 2) {
+    unsigned short byte1 = stoi(hex_encoded.substr(i, 2), nullptr, 16);
+    decoded.push_back((char)byte1);
+  }
+
+  return decoded;
+}
+
+unsigned long SwitchEndian(const string& little_endian) {
+  string big_endian;
+  for (int i = little_endian.size() - 2; i >= 0; i -= 2) {
+    big_endian += little_endian.substr(i, 2);
+  }
+
+  return stoul(big_endian, nullptr, 16);
+}
+}
Index: unittests/tools/lldb-server/tests/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/tests/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_lldb_unittest(LLDBServerTests
+  TestClient.cpp
+  TestClientException.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,28 @@
+#include <unistd.h>
+#include <string>
+#include <thread>
+#include <vector>
+
+using namespace std;
+
+int main(int argc, char* argv[])
+{
+  int thread_count = 2;
+  if (argc > 1) {
+    thread_count = stoi(argv[1], nullptr, 10);
+  }
+
+  bool delay = true;
+  vector<thread> threads;
+  for (int i = 0; i < thread_count; i++) {
+    threads.push_back(thread([&delay]{while(delay);}));
+  }
+
+  asm volatile ("int3");
+  delay = false;
+  for (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/lldb-server/.gitignore
===================================================================
--- /dev/null
+++ unittests/tools/lldb-server/.gitignore
@@ -0,0 +1 @@
+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
@@ -54,6 +54,7 @@
 endfunction()
 
 add_subdirectory(Breakpoint)
+add_subdirectory(tools)
 add_subdirectory(Core)
 add_subdirectory(Editline)
 add_subdirectory(Expression)
@@ -73,4 +74,4 @@
 
 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