Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -243,8 +243,9 @@ pass_signals = (1u << 3), auxv = (1u << 4), libraries_svr4 = (1u << 5), + savecore = (1u << 6), - LLVM_MARK_AS_BITMASK_ENUM(libraries_svr4) + LLVM_MARK_AS_BITMASK_ENUM(savecore) }; class Factory { @@ -362,6 +363,19 @@ 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 SaveCore(llvm::StringRef path_hint) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Not implemented"); + } + protected: struct SoftwareBreakpoint { uint32_t ref_count; Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -167,6 +167,8 @@ eServerPacketType_jLLDBTraceStop, eServerPacketType_jLLDBTraceGetState, eServerPacketType_jLLDBTraceGetBinaryData, + + eServerPacketType_qLLDBSaveCore, }; ServerPacketType GetServerPacketType() const; Index: lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h =================================================================== --- lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h +++ lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h @@ -88,6 +88,8 @@ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, int data = 0, int *result = nullptr); + llvm::Expected SaveCore(llvm::StringRef path_hint) override; + private: MainLoop::SignalHandleUP m_sigchld_handle; ArchSpec m_arch; Index: lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp =================================================================== --- lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp +++ lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -136,7 +136,8 @@ 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,19 @@ } } } + +llvm::Expected +NativeProcessNetBSD::SaveCore(llvm::StringRef path_hint) { + llvm::SmallString<128> path{path_hint}; + if (path.empty()) { + if (std::error_code errc = + llvm::sys::fs::createTemporaryFile("lldb", "core", path)) + return llvm::createStringError(errc, "Unable to create a temporary file"); + } + + Status error = PtraceWrapper(PT_DUMPCORE, GetID(), + const_cast(path.data()), path.size()); + if (error.Fail()) + return error.ToError(); + return path.str().str(); +} 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 @@ -214,6 +214,8 @@ PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); + PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet); + PacketResult Handle_g(StringExtractorGDBRemote &packet); void SetCurrentThreadID(lldb::tid_t tid); 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 @@ -217,6 +217,10 @@ quit = true; return this->Handle_k(packet); }); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore, + &GDBRemoteCommunicationServerLLGS::Handle_qSaveCore); } void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) { @@ -3414,6 +3418,41 @@ return SendOKResponse(); } +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 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 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)); @@ -3608,6 +3647,8 @@ ret.push_back("qXfer:auxv:read+"); if (bool(plugin_features & Extension::libraries_svr4)) ret.push_back("qXfer:libraries-svr4:read+"); + if (bool(plugin_features & Extension::savecore)) + ret.push_back("qSaveCore"); // check for client features m_extensions_supported = {}; Index: lldb/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -253,6 +253,8 @@ break; case 'S': + if (PACKET_STARTS_WITH("qSaveCore")) + return eServerPacketType_qLLDBSaveCore; if (PACKET_STARTS_WITH("qSpeedTest:")) return eServerPacketType_qSpeedTest; if (PACKET_MATCHES("qShlibInfoAddr")) Index: lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py @@ -0,0 +1,60 @@ +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__) + + @skipUnlessPlatform(oslist=["netbsd"]) + def test_netbsd_path(self): + 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() + + core_path = lldbutil.append_to_process_working_directory(self, "core") + self.test_sequence.add_log_lines([ + "read packet: $qSaveCore;path-hint:{}#00".format( + binascii.b2a_hex(core_path.encode()).decode()), + {"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() + self.assertEqual(core_path, out_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_no_path(self): + 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() + + self.test_sequence.add_log_lines([ + "read packet: $qSaveCore#00", + {"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() + + target = self.dbg.CreateTarget(None) + process = target.LoadCore(out_path) + self.assertTrue(process, PROCESS_IS_VALID) + self.assertEqual(process.GetProcessID(), procs["inferior"].pid) + os.unlink(out_path)