[Lldb-commits] [lldb] 37cbd81 - [lldb] [llgs server] Support creating core dumps on NetBSD

Michał Górny via lldb-commits lldb-commits at lists.llvm.org
Mon Sep 6 03:16:34 PDT 2021


Author: Michał Górny
Date: 2021-09-06T12:16:14+02:00
New Revision: 37cbd817d3e2b8c673862e2eb262cad6dd3dd244

URL: https://github.com/llvm/llvm-project/commit/37cbd817d3e2b8c673862e2eb262cad6dd3dd244
DIFF: https://github.com/llvm/llvm-project/commit/37cbd817d3e2b8c673862e2eb262cad6dd3dd244.diff

LOG: [lldb] [llgs server] Support creating core dumps on NetBSD

Add a new SaveCore() process method that can be used to request a core
dump.  This is currently implemented on NetBSD via the PT_DUMPCORE
ptrace(2) request, and enabled via 'savecore' extension.

Protocol-wise, a new qSaveCore packet is introduced.  It accepts zero
or more semicolon-separated key:value options, invokes the core dump
and returns a key:value response.  Currently the only option supported
is "path-hint", and the return value contains the "path" actually used.
The support for the feature is exposed via qSaveCore qSupported feature.

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

Added: 
    lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py

Modified: 
    lldb/include/lldb/Host/common/NativeProcessProtocol.h
    lldb/include/lldb/Utility/StringExtractorGDBRemote.h
    lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
    lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
    lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
    lldb/source/Utility/StringExtractorGDBRemote.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 770149e3fb28..7c3458527616 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -250,8 +250,9 @@ class NativeProcessProtocol {
     auxv = (1u << 4),
     libraries_svr4 = (1u << 5),
     memory_tagging = (1u << 6),
+    savecore = (1u << 7),
 
-    LLVM_MARK_AS_BITMASK_ENUM(memory_tagging)
+    LLVM_MARK_AS_BITMASK_ENUM(savecore)
   };
 
   class Factory {
@@ -369,6 +370,19 @@ class NativeProcessProtocol {
     m_enabled_extensions = flags;
   }
 
+  /// Write a core dump (without crashing the program).
+  ///
+  /// \param[in] path_hint
+  ///     Suggested core dump path (optional, can be empty).
+  ///
+  /// \return
+  ///     Path to the core dump if successfully written, an error
+  ///     otherwise.
+  virtual llvm::Expected<std::string> SaveCore(llvm::StringRef path_hint) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Not implemented");
+  }
+
 protected:
   struct SoftwareBreakpoint {
     uint32_t ref_count;

diff  --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index c67c05bdf182..90b2837944cd 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -170,6 +170,8 @@ class StringExtractorGDBRemote : public StringExtractor {
 
     eServerPacketType_qMemTags, // read memory tags
     eServerPacketType_QMemTags, // write memory tags
+
+    eServerPacketType_qLLDBSaveCore,
   };
 
   ServerPacketType GetServerPacketType() const;

diff  --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index c45ef5a3c82c..fd157a4b5c67 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -860,6 +860,7 @@ def add_qSupported_packets(self, client_features=[]):
         "fork-events",
         "vfork-events",
         "memory-tagging",
+        "qSaveCore",
     ]
 
     def parse_qSupported_response(self, context):

diff  --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
index 9ea1a16b8785..0420d00e39d6 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -136,7 +136,8 @@ NativeProcessNetBSD::Factory::Attach(
 NativeProcessNetBSD::Extension
 NativeProcessNetBSD::Factory::GetSupportedExtensions() const {
   return Extension::multiprocess | Extension::fork | Extension::vfork |
-         Extension::pass_signals | Extension::auxv | Extension::libraries_svr4;
+         Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
+         Extension::savecore;
 }
 
 // Public Instance Methods
@@ -1073,3 +1074,27 @@ void NativeProcessNetBSD::MonitorClone(::pid_t child_pid, bool is_vfork,
     }
   }
 }
+
+llvm::Expected<std::string>
+NativeProcessNetBSD::SaveCore(llvm::StringRef path_hint) {
+  llvm::SmallString<128> path{path_hint};
+  Status error;
+
+  // Try with the suggested path first.
+  if (!path.empty()) {
+    error = PtraceWrapper(PT_DUMPCORE, GetID(), path.data(), path.size());
+    if (!error.Fail())
+      return path.str().str();
+
+    // If the request errored, fall back to a generic temporary file.
+  }
+
+  if (std::error_code errc =
+          llvm::sys::fs::createTemporaryFile("lldb", "core", path))
+    return llvm::createStringError(errc, "Unable to create a temporary file");
+
+  error = PtraceWrapper(PT_DUMPCORE, GetID(), path.data(), path.size());
+  if (error.Fail())
+    return error.ToError();
+  return path.str().str();
+}

diff  --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
index 90d32aa6069d..3f54d02a9075 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -88,6 +88,8 @@ class NativeProcessNetBSD : public NativeProcessELF {
   static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
                               int data = 0, int *result = nullptr);
 
+  llvm::Expected<std::string> SaveCore(llvm::StringRef path_hint) override;
+
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 8e1f6bc29a6f..5d0ce3a6ef85 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -226,6 +226,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
                           quit = true;
                           return this->Handle_k(packet);
                         });
+
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore,
+      &GDBRemoteCommunicationServerLLGS::Handle_qSaveCore);
 }
 
 void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
@@ -3604,6 +3608,41 @@ GDBRemoteCommunicationServerLLGS::Handle_QMemTags(
   return status.Success() ? SendOKResponse() : SendErrorResponse(1);
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qSaveCore(
+    StringExtractorGDBRemote &packet) {
+  // Fail if we don't have a current process.
+  if (!m_current_process ||
+      (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID))
+    return SendErrorResponse(Status("Process not running."));
+
+  std::string path_hint;
+
+  StringRef packet_str{packet.GetStringRef()};
+  bool cf = packet_str.consume_front("qSaveCore");
+  assert(cf);
+  if (packet_str.consume_front(";")) {
+    llvm::SmallVector<llvm::StringRef, 2> fields;
+    packet_str.split(fields, ';');
+
+    for (auto x : fields) {
+      if (x.consume_front("path-hint:"))
+        StringExtractor(x).GetHexByteString(path_hint);
+      else
+        return SendErrorResponse(Status("Unsupported qSaveCore option"));
+    }
+  }
+
+  llvm::Expected<std::string> ret = m_current_process->SaveCore(path_hint);
+  if (!ret)
+    return SendErrorResponse(std::move(ret.takeError()));
+
+  StreamString response;
+  response.PutCString("core-path:");
+  response.PutStringAsRawHex8(ret.get());
+  return SendPacketNoLock(response.GetString());
+}
+
 void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
 
@@ -3800,6 +3839,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("qXfer:libraries-svr4:read+");
   if (bool(plugin_features & Extension::memory_tagging))
     ret.push_back("memory-tagging+");
+  if (bool(plugin_features & Extension::savecore))
+    ret.push_back("qSaveCore+");
 
   // check for client features
   m_extensions_supported = {};

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 04d0605fe420..f054e37eae94 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -214,6 +214,8 @@ class GDBRemoteCommunicationServerLLGS
 
   PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_g(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet);

diff  --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 29cf585bea56..727907595942 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -260,6 +260,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       break;
 
     case 'S':
+      if (PACKET_STARTS_WITH("qSaveCore"))
+        return eServerPacketType_qLLDBSaveCore;
       if (PACKET_STARTS_WITH("qSpeedTest:"))
         return eServerPacketType_qSpeedTest;
       if (PACKET_MATCHES("qShlibInfoAddr"))

diff  --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py
new file mode 100644
index 000000000000..405a73bcdba8
--- /dev/null
+++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py
@@ -0,0 +1,52 @@
+import gdbremote_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+import binascii
+import os
+
+class TestGdbSaveCore(gdbremote_testcase.GdbRemoteTestCaseBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    def coredump_test(self, core_path=None, expect_path=None):
+        self.build()
+        self.set_inferior_startup_attach()
+        procs = self.prep_debug_monitor_and_inferior()
+        self.add_qSupported_packets()
+        ret = self.expect_gdbremote_sequence()
+        self.assertIn("qSaveCore+", ret["qSupported_response"])
+        self.reset_test_sequence()
+
+        packet = "$qSaveCore"
+        if core_path is not None:
+            packet += ";path-hint:{}".format(
+                binascii.b2a_hex(core_path.encode()).decode())
+
+        self.test_sequence.add_log_lines([
+            "read packet: {}#00".format(packet),
+            {"direction": "send", "regex": "[$]core-path:([0-9a-f]+)#.*",
+             "capture": {1: "path"}},
+        ], True)
+        ret = self.expect_gdbremote_sequence()
+        out_path = binascii.a2b_hex(ret["path"].encode()).decode()
+        if expect_path is not None:
+            self.assertEqual(out_path, expect_path)
+
+        target = self.dbg.CreateTarget(None)
+        process = target.LoadCore(out_path)
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertEqual(process.GetProcessID(), procs["inferior"].pid)
+
+    @skipUnlessPlatform(oslist=["netbsd"])
+    def test_netbsd_path(self):
+        core = lldbutil.append_to_process_working_directory(self, "core")
+        self.coredump_test(core, core)
+
+    @skipUnlessPlatform(oslist=["netbsd"])
+    def test_netbsd_no_path(self):
+        self.coredump_test()
+
+    @skipUnlessPlatform(oslist=["netbsd"])
+    def test_netbsd_bad_path(self):
+        self.coredump_test("/dev/null/cantwritehere")


        


More information about the lldb-commits mailing list