llgs-tests: Add support for "exit" stop-reply packets
authorPavel Labath <labath@google.com>
Fri, 15 Dec 2017 15:19:45 +0000 (15:19 +0000)
committerPavel Labath <labath@google.com>
Fri, 15 Dec 2017 15:19:45 +0000 (15:19 +0000)
Summary:
This makes StopReply class abstract, so that we can represent different
types of stop replies such as StopReplyStop and StopReplyExit (there
should also be a StopReplySignal, but I don't need that right now so I
haven't implemented it yet).

This prepares the ground for a new test I'm writing.

Reviewers: eugene, zturner

Subscribers: lldb-commits

Differential Revision: https://reviews.llvm.org/D41067

llvm-svn: 320820

lldb/include/lldb/Host/Host.h
lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp
lldb/unittests/tools/lldb-server/tests/MessageObjects.h
lldb/unittests/tools/lldb-server/tests/TestClient.cpp
lldb/unittests/tools/lldb-server/tests/TestClient.h
lldb/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp

index da0b8e1..f099c72 100644 (file)
@@ -48,6 +48,12 @@ struct WaitStatus {
   static WaitStatus Decode(int wstatus);
 };
 
+inline bool operator==(WaitStatus a, WaitStatus b) {
+  return a.type == b.type && a.status == b.status;
+}
+
+inline bool operator!=(WaitStatus a, WaitStatus b) { return !(a == b); }
+
 //----------------------------------------------------------------------
 /// @class Host Host.h "lldb/Host/Host.h"
 /// @brief A class that provides host computer information.
index f396963..e5927d0 100644 (file)
@@ -133,70 +133,91 @@ const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const {
 }
 
 //====== StopReply =============================================================
-const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; }
-
-Expected<StopReply> StopReply::Create(StringRef response,
-                                      llvm::support::endianness endian) {
-  if (response.size() < 3 || !response.consume_front("T"))
+Expected<std::unique_ptr<StopReply>>
+StopReply::create(StringRef Response, llvm::support::endianness Endian) {
+  if (Response.size() < 3)
     return make_parsing_error("StopReply: Invalid packet");
+  if (Response.consume_front("T"))
+    return StopReplyStop::create(Response, Endian);
+  if (Response.consume_front("W"))
+    return StopReplyExit::create(Response);
+  return make_parsing_error("StopReply: Invalid packet");
+}
 
-  StopReply stop_reply;
-
-  StringRef signal = response.take_front(2);
-  response = response.drop_front(2);
-  if (!llvm::to_integer(signal, stop_reply.m_signal, 16))
+Expected<std::unique_ptr<StopReplyStop>>
+StopReplyStop::create(StringRef Response, llvm::support::endianness Endian) {
+  unsigned int Signal;
+  StringRef SignalStr = Response.take_front(2);
+  Response = Response.drop_front(2);
+  if (!to_integer(SignalStr, Signal, 16))
     return make_parsing_error("StopReply: stop signal");
 
-  auto elements = SplitPairList(response);
-  for (StringRef field :
+  auto Elements = SplitPairList(Response);
+  for (StringRef Field :
        {"name", "reason", "thread", "threads", "thread-pcs"}) {
     // This will insert an empty field if there is none. In the future, we
     // should probably differentiate between these fields not being present and
     // them being empty, but right now no tests depends on this.
-    if (elements.insert({field, {""}}).first->second.size() != 1)
+    if (Elements.insert({Field, {""}}).first->second.size() != 1)
       return make_parsing_error(
-          "StopReply: got multiple responses for the {0} field", field);
+          "StopReply: got multiple responses for the {0} field", Field);
   }
-  stop_reply.m_name = elements["name"][0];
-  stop_reply.m_reason = elements["reason"][0];
+  StringRef Name = Elements["name"][0];
+  StringRef Reason = Elements["reason"][0];
 
-  if (!llvm::to_integer(elements["thread"][0], stop_reply.m_thread, 16))
+  lldb::tid_t Thread;
+  if (!to_integer(Elements["thread"][0], Thread, 16))
     return make_parsing_error("StopReply: thread");
 
-  SmallVector<StringRef, 20> threads;
-  SmallVector<StringRef, 20> pcs;
-  elements["threads"][0].split(threads, ',');
-  elements["thread-pcs"][0].split(pcs, ',');
-  if (threads.size() != pcs.size())
+  SmallVector<StringRef, 20> Threads;
+  SmallVector<StringRef, 20> Pcs;
+  Elements["threads"][0].split(Threads, ',');
+  Elements["thread-pcs"][0].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;
+  U64Map ThreadPcs;
+  for (auto ThreadPc : zip(Threads, Pcs)) {
+    lldb::tid_t Id;
+    uint64_t Pc;
+    if (!to_integer(std::get<0>(ThreadPc), Id, 16))
+      return make_parsing_error("StopReply: Thread id '{0}'",
+                                std::get<0>(ThreadPc));
+    if (!to_integer(std::get<1>(ThreadPc), Pc, 16))
+      return make_parsing_error("StopReply Thread Pc '{0}'",
+                                std::get<1>(ThreadPc));
+
+    ThreadPcs[Id] = Pc;
   }
 
-  for (auto i = elements.begin(); i != elements.end(); i++) {
-    StringRef key = i->getKey();
-    const auto &val = i->getValue();
-    if (key.size() == 2) {
-      unsigned int reg;
-      if (key.getAsInteger(16, reg))
-        continue;
-      if (val.size() != 1)
-        return make_parsing_error(
-            "StopReply: multiple entries for register field [{0:x}]", reg);
-
-      stop_reply.m_registers[reg] = val[0].str();
-    }
+  RegisterMap Registers;
+  for (const auto &E : Elements) {
+    StringRef Key = E.getKey();
+    const auto &Val = E.getValue();
+    if (Key.size() != 2)
+      continue;
+
+    unsigned int Reg;
+    if (!to_integer(Key, Reg, 16))
+      continue;
+
+    if (Val.size() != 1)
+      return make_parsing_error(
+          "StopReply: multiple entries for register field [{0:x}]", Reg);
+
+    Registers[Reg] = Val[0].str();
   }
 
-  return stop_reply;
+  return llvm::make_unique<StopReplyStop>(Signal, Thread, Name, ThreadPcs,
+                                          Registers, Reason);
+}
+
+Expected<std::unique_ptr<StopReplyExit>>
+StopReplyExit::create(StringRef Response) {
+  uint8_t Status;
+  if (!to_integer(Response, Status, 16))
+    return make_parsing_error("StopReply: exit status");
+  return llvm::make_unique<StopReplyExit>(Status);
 }
 
 //====== Globals ===============================================================
index ec61258..bbb7f75 100644 (file)
@@ -10,6 +10,7 @@
 #ifndef LLDB_SERVER_TESTS_MESSAGEOBJECTS_H
 #define LLDB_SERVER_TESTS_MESSAGEOBJECTS_H
 
+#include "lldb/Host/Host.h"
 #include "lldb/lldb-types.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallString.h"
@@ -74,20 +75,67 @@ private:
 
 class StopReply {
 public:
-  static llvm::Expected<StopReply> Create(llvm::StringRef response,
-                                          llvm::support::endianness endian);
-  const U64Map &GetThreadPcs() const;
+  StopReply() = default;
+  virtual ~StopReply() = default;
+
+  static llvm::Expected<std::unique_ptr<StopReply>>
+  create(llvm::StringRef response, llvm::support::endianness endian);
+
+  // for llvm::cast<>
+  virtual lldb_private::WaitStatus getKind() const = 0;
+
+  StopReply(const StopReply &) = delete;
+  void operator=(const StopReply &) = delete;
+};
+
+class StopReplyStop : public StopReply {
+public:
+  StopReplyStop(uint8_t Signal, lldb::tid_t ThreadId, llvm::StringRef Name,
+                U64Map ThreadPcs, RegisterMap Registers, llvm::StringRef Reason)
+      : Signal(Signal), ThreadId(ThreadId), Name(Name),
+        ThreadPcs(std::move(ThreadPcs)), Registers(std::move(Registers)),
+        Reason(Reason) {}
+
+  static llvm::Expected<std::unique_ptr<StopReplyStop>>
+  create(llvm::StringRef response, llvm::support::endianness endian);
+
+  const U64Map &getThreadPcs() const { return ThreadPcs; }
+  lldb::tid_t getThreadId() const { return ThreadId; }
+
+  // for llvm::cast<>
+  lldb_private::WaitStatus getKind() const override {
+    return lldb_private::WaitStatus{lldb_private::WaitStatus::Stop, Signal};
+  }
+  static bool classof(const StopReply *R) {
+    return R->getKind().type == lldb_private::WaitStatus::Stop;
+  }
 
 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;
+  uint8_t Signal;
+  lldb::tid_t ThreadId;
+  std::string Name;
+  U64Map ThreadPcs;
+  RegisterMap Registers;
+  std::string Reason;
+};
+
+class StopReplyExit : public StopReply {
+public:
+  explicit StopReplyExit(uint8_t Status) : Status(Status) {}
+
+  static llvm::Expected<std::unique_ptr<StopReplyExit>>
+  create(llvm::StringRef response);
+
+  // for llvm::cast<>
+  lldb_private::WaitStatus getKind() const override {
+    return lldb_private::WaitStatus{lldb_private::WaitStatus::Exit, Status};
+  }
+  static bool classof(const StopReply *R) {
+    return R->getKind().type == lldb_private::WaitStatus::Exit;
+  }
+
+private:
+  uint8_t Status;
 };
 
 // Common functions for parsing packet data.
index 39b5cc0..c414eee 100644 (file)
@@ -154,7 +154,8 @@ Optional<JThreadsInfo> TestClient::GetJThreadsInfo() {
 }
 
 const StopReply &TestClient::GetLatestStopReply() {
-  return m_stop_reply.getValue();
+  assert(m_stop_reply);
+  return *m_stop_reply;
 }
 
 Error TestClient::SendMessage(StringRef message) {
@@ -236,7 +237,7 @@ Error TestClient::Continue(StringRef message) {
   std::string response;
   if (Error E = SendMessage(message, response))
     return E;
-  auto creation = StopReply::Create(response, m_process_info->GetEndian());
+  auto creation = StopReply::create(response, m_process_info->GetEndian());
   if (Error E = creation.takeError())
     return E;
 
index 45e2208..4fa1bbb 100644 (file)
@@ -16,6 +16,8 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Connection.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/FormatVariadic.h"
 #include <memory>
 #include <string>
 
@@ -44,6 +46,14 @@ public:
   const ProcessInfo &GetProcessInfo();
   llvm::Optional<JThreadsInfo> GetJThreadsInfo();
   const StopReply &GetLatestStopReply();
+  template <typename T> llvm::Expected<const T &> GetLatestStopReplyAs() {
+    assert(m_stop_reply);
+    if (const auto *Reply = llvm::dyn_cast<T>(m_stop_reply.get()))
+      return *Reply;
+    return llvm::make_error<llvm::StringError>(
+        llvm::formatv("Unexpected Stop Reply {0}", m_stop_reply->getKind()),
+        llvm::inconvertibleErrorCode());
+  }
   llvm::Error SendMessage(llvm::StringRef message);
   llvm::Error SendMessage(llvm::StringRef message,
                           std::string &response_string);
@@ -62,7 +72,7 @@ private:
           result);
 
   llvm::Optional<ProcessInfo> m_process_info;
-  llvm::Optional<StopReply> m_stop_reply;
+  std::unique_ptr<StopReply> m_stop_reply;
   unsigned int m_pc_register = UINT_MAX;
 };
 
index c897271..6ff777b 100644 (file)
@@ -31,8 +31,9 @@ TEST_F(StandardStartupTest, TestStopReplyContainsThreadPcs) {
   auto jthreads_info = Client->GetJThreadsInfo();
   ASSERT_TRUE(jthreads_info);
 
-  auto stop_reply = Client->GetLatestStopReply();
-  auto stop_reply_pcs = stop_reply.GetThreadPcs();
+  auto stop_reply = Client->GetLatestStopReplyAs<StopReplyStop>();
+  ASSERT_THAT_EXPECTED(stop_reply, Succeeded());
+  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.";