Index: lldb/trunk/unittests/tools/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/tools/CMakeLists.txt +++ lldb/trunk/unittests/tools/CMakeLists.txt @@ -1,3 +1,3 @@ -if(CMAKE_SYSTEM_NAME MATCHES "Android|Linux|NetBSD") +if(CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|Linux|NetBSD") add_subdirectory(lldb-server) endif() Index: lldb/trunk/unittests/tools/lldb-server/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/tools/lldb-server/CMakeLists.txt +++ lldb/trunk/unittests/tools/lldb-server/CMakeLists.txt @@ -7,7 +7,12 @@ add_lldb_test_executable(thread_inferior inferior/thread_inferior.cpp) -add_definitions(-DLLDB_SERVER="$") +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + add_definitions(-DLLDB_SERVER="$") +else() + add_definitions(-DLLDB_SERVER="$") +endif() + add_definitions(-DTHREAD_INFERIOR="${CMAKE_CURRENT_BINARY_DIR}/thread_inferior") add_subdirectory(tests) add_dependencies(LLDBServerTests thread_inferior) 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 @@ -89,7 +89,10 @@ // Common functions for parsing packet data. llvm::Expected> -SplitPairList(llvm::StringRef caller, llvm::StringRef s); +SplitUniquePairList(llvm::StringRef caller, llvm::StringRef s); + +llvm::StringMap> +SplitPairList(llvm::StringRef s); template llvm::Error make_parsing_error(llvm::StringRef format, Args &&... args) { 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 @@ -19,7 +19,7 @@ Expected ProcessInfo::Create(StringRef response) { ProcessInfo process_info; - auto elements_or_error = SplitPairList("ProcessInfo", response); + auto elements_or_error = SplitUniquePairList("ProcessInfo", response); if (!elements_or_error) return elements_or_error.takeError(); @@ -140,21 +140,36 @@ Expected StopReply::Create(StringRef response, llvm::support::endianness endian) { + if (response.size() < 3 || !response.consume_front("T")) + return make_parsing_error("StopReply: Invalid packet"); + StopReply stop_reply; - auto elements_or_error = SplitPairList("StopReply", response); - if (auto split_error = elements_or_error.takeError()) { - return std::move(split_error); + StringRef signal = response.take_front(2); + response = response.drop_front(2); + if (!llvm::to_integer(signal, stop_reply.m_signal, 16)) + return make_parsing_error("StopReply: stop signal"); + + 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) + return make_parsing_error( + "StopReply: got multiple responses for the {0} field", field); } + stop_reply.m_name = elements["name"][0]; + stop_reply.m_reason = elements["reason"][0]; - auto elements = *elements_or_error; - stop_reply.m_name = elements["name"]; - stop_reply.m_reason = elements["reason"]; + if (!llvm::to_integer(elements["thread"][0], stop_reply.m_thread, 16)) + return make_parsing_error("StopReply: thread"); SmallVector threads; SmallVector pcs; - elements["threads"].split(threads, ','); - elements["thread-pcs"].split(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"); @@ -171,17 +186,16 @@ 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.m_thread)) - return make_parsing_error("StopReply: thread id"); - if (key.substr(1, 2).getAsInteger(16, stop_reply.m_signal)) - return make_parsing_error("StopReply: stop signal"); - } else if (key.size() == 2) { + const auto &val = i->getValue(); + if (key.size() == 2) { unsigned int reg; - if (!key.getAsInteger(16, reg)) { - stop_reply.m_registers[reg] = val.str(); - } + 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(); } } @@ -189,7 +203,8 @@ } //====== Globals =============================================================== -Expected> SplitPairList(StringRef caller, StringRef str) { +Expected> SplitUniquePairList(StringRef caller, + StringRef str) { SmallVector elements; str.split(elements, ';'); @@ -199,7 +214,20 @@ if (pairs.count(pair.first)) return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first); - pairs.insert(s.split(':')); + pairs.insert(pair); + } + + return pairs; +} + +StringMap> SplitPairList(StringRef str) { + SmallVector elements; + str.split(elements, ';'); + + StringMap> pairs; + for (StringRef s : elements) { + std::pair pair = s.split(':'); + pairs[pair.first].push_back(pair.second); } return pairs; 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 @@ -22,6 +22,9 @@ : public lldb_private::process_gdb_remote::GDBRemoteCommunicationClient { public: static void Initialize(); + static bool IsDebugServer(); + static bool IsLldbServer(); + TestClient(const std::string &test_name, const std::string &test_case_name); virtual ~TestClient(); LLVM_NODISCARD bool StartDebugger(); 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 @@ -15,6 +15,7 @@ #include "lldb/Interpreter/Args.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" #include "gtest/gtest.h" #include #include @@ -28,6 +29,12 @@ namespace llgs_tests { void TestClient::Initialize() { HostInfo::Initialize(); } +bool TestClient::IsDebugServer() { + return sys::path::filename(LLDB_SERVER).contains("debugserver"); +} + +bool TestClient::IsLldbServer() { return !IsDebugServer(); } + TestClient::TestClient(const std::string &test_name, const std::string &test_case_name) : m_test_name(test_name), m_test_case_name(test_case_name), @@ -39,13 +46,16 @@ const ArchSpec &arch_spec = HostInfo::GetArchitecture(); Args args; args.AppendArgument(LLDB_SERVER); - args.AppendArgument("gdbserver"); - args.AppendArgument("--log-channels=gdb-remote packets"); + if (IsLldbServer()) { + args.AppendArgument("gdbserver"); + args.AppendArgument("--log-channels=gdb-remote packets"); + } else { + args.AppendArgument("--log-flags=0x800000"); + } args.AppendArgument("--reverse-connect"); std::string log_file_name = GenerateLogFileName(arch_spec); - if (log_file_name.size()) { + if (log_file_name.size()) args.AppendArgument("--log-file=" + log_file_name); - } Status error; TCPSocket listen_socket(true, false); @@ -83,7 +93,11 @@ bool TestClient::StopDebugger() { std::string response; - return SendMessage("k", response, PacketResult::ErrorDisconnected); + // Debugserver (non-conformingly?) sends a reply to the k packet instead of + // simply closing the connection. + PacketResult result = + IsDebugServer() ? PacketResult::Success : PacketResult::ErrorDisconnected; + return SendMessage("k", response, result); } bool TestClient::SetInferior(llvm::ArrayRef inferior_args) { @@ -192,7 +206,7 @@ return UINT_MAX; } - auto elements_or_error = SplitPairList("GetPcRegisterId", response); + auto elements_or_error = SplitUniquePairList("GetPcRegisterId", response); if (auto split_error = elements_or_error.takeError()) { GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " << response;