mgorny updated this revision to Diff 330674.
mgorny added a comment.

clang-format.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D98482/new/

https://reviews.llvm.org/D98482

Files:
  lldb/include/lldb/Utility/GDBRemoteError.h
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  lldb/source/Utility/CMakeLists.txt
  lldb/source/Utility/GDBRemoteError.cpp
  lldb/source/Utility/Status.cpp
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
  lldb/unittests/Utility/CMakeLists.txt
  lldb/unittests/Utility/StringExtractorGDBRemoteTest.cpp

Index: lldb/unittests/Utility/StringExtractorGDBRemoteTest.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Utility/StringExtractorGDBRemoteTest.cpp
@@ -0,0 +1,175 @@
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <limits.h>
+
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+#include "lldb/lldb-defines.h"
+
+namespace {
+class StringExtractorGDBRemoteTest : public ::testing::Test {};
+} // namespace
+
+TEST_F(StringExtractorGDBRemoteTest, GetPidTid) {
+  StringExtractorGDBRemote ex("");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  // invalid/short values
+
+  ex.Reset("narf");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset(";1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset(".1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("pnarf");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p;1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p.1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p1234.");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p1234.;1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("-2");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p1234.-2");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p-2");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p-2.1234");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  // overflow
+
+  ex.Reset("p10000000000000000");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p10000000000000000.0");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("10000000000000000");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p0.10000000000000000");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  ex.Reset("p10000000000000000.10000000000000000");
+  EXPECT_EQ(ex.GetPidTid(0), llvm::None);
+
+  // pure thread id
+
+  ex.Reset("0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(), ::testing::Pair(100, 0));
+
+  ex.Reset("-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(100, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("1234");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(), ::testing::Pair(100, 0x1234ULL));
+
+  ex.Reset("123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(100, 0x123456789ABCDEF0ULL));
+
+  // pure process id
+
+  ex.Reset("p0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p1234");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x1234ULL, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, LLDB_INVALID_THREAD_ID));
+
+  // combined thread id + process id
+
+  ex.Reset("p0.0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(), ::testing::Pair(0, 0));
+
+  ex.Reset("p0.-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0, LLDB_INVALID_THREAD_ID));
+
+  // NB: technically, specific thread with unspecified process is invalid
+  // but we do not filter that in StringExtractor
+
+  ex.Reset("p0.1234");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(), ::testing::Pair(0, 0x1234ULL));
+
+  ex.Reset("p0.123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0, 0x123456789ABCDEF0ULL));
+
+  ex.Reset("p-1.0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(LLDB_INVALID_PROCESS_ID, 0));
+
+  ex.Reset("p-1.-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p-1.1234");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(LLDB_INVALID_PROCESS_ID, 0x1234ULL));
+
+  ex.Reset("p-1.123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(LLDB_INVALID_PROCESS_ID, 0x123456789ABCDEF0ULL));
+
+  ex.Reset("p1234.0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(), ::testing::Pair(0x1234ULL, 0));
+
+  ex.Reset("p1234.-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x1234ULL, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p1234.123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x1234ULL, 0x123456789ABCDEF0ULL));
+
+  ex.Reset("p123456789ABCDEF0.0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, 0));
+
+  ex.Reset("p123456789ABCDEF0.-1");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, LLDB_INVALID_THREAD_ID));
+
+  ex.Reset("p123456789ABCDEF0.1234");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, 0x1234ULL));
+
+  ex.Reset("p123456789ABCDEF0.123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, 0x123456789ABCDEF0ULL));
+
+  ex.Reset("p123456789ABCDEF0.123456789ABCDEF0");
+  EXPECT_THAT(ex.GetPidTid(100).getValue(),
+              ::testing::Pair(0x123456789ABCDEF0ULL, 0x123456789ABCDEF0ULL));
+}
Index: lldb/unittests/Utility/CMakeLists.txt
===================================================================
--- lldb/unittests/Utility/CMakeLists.txt
+++ lldb/unittests/Utility/CMakeLists.txt
@@ -29,6 +29,7 @@
   StatusTest.cpp
   StreamTeeTest.cpp
   StreamTest.cpp
+  StringExtractorGDBRemoteTest.cpp
   StringExtractorTest.cpp
   StringLexerTest.cpp
   StringListTest.cpp
Index: lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
===================================================================
--- lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
+++ lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
@@ -349,7 +349,7 @@
             # Increment loop
             reg_index += 1
 
-    def Hg_switches_to_3_threads(self):
+    def Hg_switches_to_3_threads(self, pass_pid=False):
         # Startup the inferior with three threads (main + 2 new ones).
         procs = self.prep_debug_monitor_and_inferior(
             inferior_args=["thread:new", "thread:new"])
@@ -363,12 +363,16 @@
         threads = self.wait_for_thread_count(3)
         self.assertEqual(len(threads), 3)
 
+        pid_str = ""
+        if pass_pid:
+            pid_str = "p{0:x}.".format(procs["inferior"].pid)
+
         # verify we can $H to each thead, and $qC matches the thread we set.
         for thread in threads:
             # Change to each thread, verify current thread id.
             self.reset_test_sequence()
             self.test_sequence.add_log_lines(
-                ["read packet: $Hg{0:x}#00".format(thread),  # Set current thread.
+                ["read packet: $Hg{0}{1:x}#00".format(pid_str, thread),  # Set current thread.
                  "send packet: $OK#00",
                  "read packet: $qC#00",
                  {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}],
@@ -393,6 +397,50 @@
         self.set_inferior_startup_attach()
         self.Hg_switches_to_3_threads()
 
+    @expectedFailureAll(oslist=["windows"]) # expect 4 threads
+    def test_Hg_switches_to_3_threads_attach_pass_correct_pid(self):
+        self.build()
+        self.set_inferior_startup_attach()
+        self.Hg_switches_to_3_threads(pass_pid=True)
+
+    def Hg_fails_on_pid(self, pass_pid):
+        # Start the inferior.
+        procs = self.prep_debug_monitor_and_inferior(
+            inferior_args=["thread:new"])
+
+        self.run_process_then_stop(run_seconds=1)
+
+        threads = self.wait_for_thread_count(2)
+        self.assertEqual(len(threads), 2)
+
+        if pass_pid == -1:
+            pid_str = "p-1."
+        else:
+            pid_str = "p{0:x}.".format(pass_pid)
+        thread = threads[1]
+
+        self.test_sequence.add_log_lines(
+            ["read packet: $Hg{0}{1:x}#00".format(pid_str, thread),  # Set current thread.
+             "send packet: $E15#00"],
+            True)
+
+        self.expect_gdbremote_sequence()
+
+    def test_Hg_fails_on_another_pid(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.Hg_fails_on_pid(1)
+
+    def test_Hg_fails_on_zero_pid(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.Hg_fails_on_pid(0)
+
+    def test_Hg_fails_on_minus_one_pid(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.Hg_fails_on_pid(-1)
+
     def Hc_then_Csignal_signals_correct_thread(self, segfault_signo):
         # NOTE only run this one in inferior-launched mode: we can't grab inferior stdout when running attached,
         # and the test requires getting stdout from the exe.
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -606,3 +606,51 @@
   else
     return true; // No validator, so response is valid
 }
+
+llvm::Optional<std::pair<lldb::pid_t, lldb::tid_t>>
+StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) {
+  llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index);
+  lldb::pid_t pid = default_pid;
+  lldb::tid_t tid;
+
+  if (view.consume_front("p")) {
+    // process identifier
+    ++m_index;
+    if (view.consume_front("-1")) {
+      // -1 is a special case
+      m_index += 2;
+      pid = LLDB_INVALID_PROCESS_ID;
+    } else {
+      uint64_t prev_index = m_index;
+      pid = GetHexMaxU64(false, 0);
+      if (m_index == prev_index || m_index == UINT64_MAX) {
+        // if index was not incremented or we overflowed, it failed
+        m_index = UINT64_MAX;
+        return llvm::None;
+      }
+      view = view.substr(m_index - prev_index);
+    }
+
+    // "." must follow if we expect TID too; otherwise, we assume -1
+    if (!view.consume_front("."))
+      return {{pid, LLDB_INVALID_THREAD_ID}};
+    ++m_index;
+  }
+
+  // thread identifier
+  if (view.consume_front("-1")) {
+    // -1 is a special case
+    m_index += 2;
+    tid = LLDB_INVALID_THREAD_ID;
+  } else {
+    uint64_t prev_index = m_index;
+    tid = GetHexMaxU64(false, 0);
+    if (m_index == prev_index || m_index == UINT64_MAX) {
+      // if index was not incremented or we overflowed, it failed
+      // (also reset pid since the value is incorrect)
+      m_index = UINT64_MAX;
+      return llvm::None;
+    }
+  }
+  return {{pid, tid}};
+}
Index: lldb/source/Utility/Status.cpp
===================================================================
--- lldb/source/Utility/Status.cpp
+++ lldb/source/Utility/Status.cpp
@@ -8,6 +8,7 @@
 
 #include "lldb/Utility/Status.h"
 
+#include "lldb/Utility/GDBRemoteError.h"
 #include "lldb/Utility/VASPrintf.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
@@ -68,7 +69,14 @@
 
   // if the error happens to be a errno error, preserve the error code
   error = llvm::handleErrors(
-      std::move(error), [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error {
+      std::move(error),
+      [&](std::unique_ptr<GDBRemoteError> e) -> llvm::Error {
+        m_code = e->getCode();
+        m_type = ErrorType::eErrorTypeGeneric;
+        SetErrorString(e->getMsg());
+        return llvm::Error::success();
+      },
+      [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error {
         std::error_code ec = e->convertToErrorCode();
         if (ec.category() == std::generic_category()) {
           m_code = ec.value();
Index: lldb/source/Utility/GDBRemoteError.cpp
===================================================================
--- /dev/null
+++ lldb/source/Utility/GDBRemoteError.cpp
@@ -0,0 +1,11 @@
+//===-- GDBRemoteError.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/GDBRemoteError.h"
+
+char lldb_private::GDBRemoteError::ID;
Index: lldb/source/Utility/CMakeLists.txt
===================================================================
--- lldb/source/Utility/CMakeLists.txt
+++ lldb/source/Utility/CMakeLists.txt
@@ -39,6 +39,7 @@
   Event.cpp
   FileSpec.cpp
   GDBRemote.cpp
+  GDBRemoteError.cpp
   IOObject.cpp
   LLDBAssert.cpp
   Listener.cpp
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -244,6 +244,17 @@
 
   void StopSTDIOForwarding();
 
+  // Read thread-id from packet.  If the thread-id is correct, returns it.
+  // Otherwise, returns the error.
+  //
+  // If allow_any is true, then the pid/tid value of 0 ('any') will be allowed.
+  // If allow_all is true, then the pid/tid value of -1 ('all') will be allowed.
+  // In any case, the function assumes that exactly one inferior is being
+  // debugged and rejects pid values that do no match that inferior.
+  llvm::Expected<lldb::tid_t> ReadTid(StringExtractorGDBRemote &packet,
+                                      bool allow_any = false,
+                                      bool allow_all = false);
+
   // For GDBRemoteCommunicationServerLLGS only
   GDBRemoteCommunicationServerLLGS(const GDBRemoteCommunicationServerLLGS &) =
       delete;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -32,6 +32,7 @@
 #include "lldb/Utility/DataBuffer.h"
 #include "lldb/Utility/Endian.h"
 #include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/GDBRemoteError.h"
 #include "lldb/Utility/LLDBAssert.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/RegisterValue.h"
@@ -2220,10 +2221,11 @@
   }
 
   // Parse out the thread number.
-  // FIXME return a parse success/fail value.  All values are valid here.
-  const lldb::tid_t tid =
-      packet.GetHexMaxU64(false, std::numeric_limits<lldb::tid_t>::max());
+  llvm::Expected<lldb::tid_t> tid_ret = ReadTid(packet, /*allow_any=*/true, /*allow_all=*/true);
+  if (!tid_ret)
+    return SendErrorResponse(tid_ret.takeError());
 
+  lldb::tid_t tid = tid_ret.get();
   // Ensure we have the given thread when not specifying -1 (all threads) or 0
   // (any thread).
   if (tid != LLDB_INVALID_THREAD_ID && tid != 0) {
@@ -3653,3 +3655,39 @@
   }
   return result;
 }
+
+llvm::Expected<lldb::tid_t>
+GDBRemoteCommunicationServerLLGS::ReadTid(StringExtractorGDBRemote &packet,
+                                          bool allow_any, bool allow_all) {
+  assert(m_debugged_process_up);
+  assert(m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID);
+
+  auto pid_tid = packet.GetPidTid(m_debugged_process_up->GetID());
+  if (!pid_tid)
+    return llvm::make_error<GDBRemoteError>(0x03, "Malformed thread-id");
+
+  lldb::pid_t pid = pid_tid->first;
+  lldb::tid_t tid = pid_tid->second;
+
+  if ((!allow_any && pid == 0) ||
+      (!allow_all && pid == LLDB_INVALID_PROCESS_ID))
+    return llvm::make_error<GDBRemoteError>(
+        0x15, llvm::formatv("PID value {0} not allowed", pid == 0 ? 0 : -1));
+
+  if ((!allow_any && tid == 0) || (!allow_all && tid == LLDB_INVALID_THREAD_ID))
+    return llvm::make_error<GDBRemoteError>(
+        0x15, llvm::formatv("TID value {0} not allowed", tid == 0 ? 0 : -1));
+
+  if (pid != 0 && pid != LLDB_INVALID_PROCESS_ID) {
+    if (pid != m_debugged_process_up->GetID())
+      return llvm::make_error<GDBRemoteError>(
+          0x15, llvm::formatv("PID {0} not debugged", pid));
+  } else { // pid == 0 || pid == LLDB_INVALID_PROCESS_ID
+    if (tid != 0 && tid != LLDB_INVALID_THREAD_ID)
+      return llvm::make_error<GDBRemoteError>(
+          0x15, llvm::formatv("TID value {0} not allowed for pid value {1}",
+                              tid, pid == 0 ? 0 : -1));
+  }
+
+  return tid;
+}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -848,6 +848,7 @@
   response.PutCString(";qXfer:auxv:read+");
   response.PutCString(";qXfer:libraries-svr4:read+");
 #endif
+  response.PutCString(";multiprocess+");
 
   return SendPacketNoLock(response.GetString());
 }
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -558,6 +558,7 @@
   LazyBool m_supports_jGetSharedCacheInfo;
   LazyBool m_supports_QPassSignals;
   LazyBool m_supports_error_string_reply;
+  LazyBool m_supports_multiprocess;
 
   bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
       m_supports_qUserName : 1, m_supports_qGroupName : 1,
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -89,6 +89,7 @@
       m_supports_jGetSharedCacheInfo(eLazyBoolCalculate),
       m_supports_QPassSignals(eLazyBoolCalculate),
       m_supports_error_string_reply(eLazyBoolCalculate),
+      m_supports_multiprocess(eLazyBoolCalculate),
       m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true),
       m_supports_qUserName(true), m_supports_qGroupName(true),
       m_supports_qThreadStopInfo(true), m_supports_z0(true),
@@ -292,6 +293,7 @@
     m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
     m_attach_or_wait_reply = eLazyBoolCalculate;
     m_avoid_g_packets = eLazyBoolCalculate;
+    m_supports_multiprocess = eLazyBoolCalculate;
     m_supports_qXfer_auxv_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
@@ -342,11 +344,13 @@
   m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
   m_supports_qXfer_features_read = eLazyBoolNo;
   m_supports_qXfer_memory_map_read = eLazyBoolNo;
+  m_supports_multiprocess = eLazyBoolNo;
   m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
                                   // not, we assume no limit
 
   // build the qSupported packet
-  std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc"};
+  std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc",
+                                       "multiprocess+"};
   StreamString packet;
   packet.PutCString("qSupported");
   for (uint32_t i = 0; i < features.size(); ++i) {
@@ -433,6 +437,11 @@
     else
       m_supports_QPassSignals = eLazyBoolNo;
 
+    if (::strstr(response_cstr, "multiprocess+"))
+      m_supports_multiprocess = eLazyBoolYes;
+    else
+      m_supports_multiprocess = eLazyBoolNo;
+
     const char *packet_size_str = ::strstr(response_cstr, "PacketSize=");
     if (packet_size_str) {
       StringExtractorGDBRemote packet_response(packet_size_str +
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -902,7 +902,8 @@
         "qXfer:libraries-svr4:read",
         "qXfer:features:read",
         "qEcho",
-        "QPassSignals"
+        "QPassSignals",
+        "multiprocess",
     ]
 
     def parse_qSupported_response(self, context):
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -193,6 +193,12 @@
 
   size_t GetEscapedBinaryData(std::string &str);
 
+  // Read thread-id from the packet.  If the packet is valid, returns
+  // the pair (PID, TID), otherwise returns llvm::None.  If the packet
+  // does not list a PID, default_pid is used.
+  llvm::Optional<std::pair<lldb::pid_t, lldb::tid_t>>
+  GetPidTid(lldb::pid_t default_pid);
+
 protected:
   ResponseValidatorCallback m_validator;
   void *m_validator_baton;
Index: lldb/include/lldb/Utility/GDBRemoteError.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Utility/GDBRemoteError.h
@@ -0,0 +1,38 @@
+//===-- GDBRemoteError.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_GDBREMOTEERROR_H
+#define LLDB_UTILITY_GDBREMOTEERROR_H
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+class GDBRemoteError : public llvm::ErrorInfo<GDBRemoteError> {
+public:
+  static char ID;
+
+  GDBRemoteError(uint8_t NewCode, const std::string &NewMsg)
+      : Code(NewCode), Msg(NewMsg){};
+
+  void log(llvm::raw_ostream &OS) const override { OS << Msg; }
+
+  std::error_code convertToErrorCode() const override {
+    return llvm::errc::not_supported;
+  };
+
+  uint8_t getCode() { return Code; }
+  const std::string &getMsg() { return Msg; }
+
+private:
+  uint8_t Code;
+  std::string Msg;
+};
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_GDBREMOTEERROR_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to