Index: lldb/trunk/include/lldb/Host/Host.h =================================================================== --- lldb/trunk/include/lldb/Host/Host.h +++ lldb/trunk/include/lldb/Host/Host.h @@ -48,6 +48,12 @@ 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: lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h +++ lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h @@ -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 @@ class StopReply { public: - static llvm::Expected Create(llvm::StringRef response, - llvm::support::endianness endian); - const U64Map &GetThreadPcs() const; + StopReply() = default; + virtual ~StopReply() = default; + + static llvm::Expected> + 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> + 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> + 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: lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp @@ -133,70 +133,91 @@ } //====== StopReply ============================================================= -const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; } - -Expected StopReply::Create(StringRef response, - llvm::support::endianness endian) { - if (response.size() < 3 || !response.consume_front("T")) +Expected> +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> +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 threads; - SmallVector pcs; - elements["threads"][0].split(threads, ','); - elements["thread-pcs"][0].split(pcs, ','); - if (threads.size() != pcs.size()) + SmallVector Threads; + SmallVector 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; - } - - 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); + 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; + } + + 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; - stop_reply.m_registers[reg] = val[0].str(); - } + 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(Signal, Thread, Name, ThreadPcs, + Registers, Reason); +} + +Expected> +StopReplyExit::create(StringRef Response) { + uint8_t Status; + if (!to_integer(Response, Status, 16)) + return make_parsing_error("StopReply: exit status"); + return llvm::make_unique(Status); } //====== Globals =============================================================== Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h +++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h @@ -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 #include @@ -44,6 +46,14 @@ const ProcessInfo &GetProcessInfo(); llvm::Optional GetJThreadsInfo(); const StopReply &GetLatestStopReply(); + template llvm::Expected GetLatestStopReplyAs() { + assert(m_stop_reply); + if (const auto *Reply = llvm::dyn_cast(m_stop_reply.get())) + return *Reply; + return llvm::make_error( + 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 @@ result); llvm::Optional m_process_info; - llvm::Optional m_stop_reply; + std::unique_ptr m_stop_reply; unsigned int m_pc_register = UINT_MAX; }; Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp @@ -154,7 +154,8 @@ } 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 @@ 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: lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp @@ -31,8 +31,9 @@ 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(); + 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.";