aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Górny <mgorny@moritz.systems>2021-04-26 13:47:02 +0200
committerMichał Górny <mgorny@moritz.systems>2021-09-06 12:16:14 +0200
commit37cbd817d3e2b8c673862e2eb262cad6dd3dd244 (patch)
tree57262d6e7bbc9c8d89a3a88be8887c0b64fee23d
parentfae0dfa6421ea6c02f86ba7292fa782e1e2b69d1 (diff)
downloadllvm-37cbd817d3e2b8c673862e2eb262cad6dd3dd244.zip
llvm-37cbd817d3e2b8c673862e2eb262cad6dd3dd244.tar.gz
llvm-37cbd817d3e2b8c673862e2eb262cad6dd3dd244.tar.bz2
[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
-rw-r--r--lldb/include/lldb/Host/common/NativeProcessProtocol.h16
-rw-r--r--lldb/include/lldb/Utility/StringExtractorGDBRemote.h2
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py1
-rw-r--r--lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp27
-rw-r--r--lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h2
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp41
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h2
-rw-r--r--lldb/source/Utility/StringExtractorGDBRemote.cpp2
-rw-r--r--lldb/test/API/tools/lldb-server/TestGdbRemoteSaveCore.py52
9 files changed, 143 insertions, 2 deletions
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 770149e..7c34585 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -250,8 +250,9 @@ public:
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 @@ public:
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 c67c05b..90b2837 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -170,6 +170,8 @@ public:
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 c45ef5a..fd157a4 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 @@ class GdbRemoteTestCaseBase(Base):
"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 9ea1a16..0420d00 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 90d32aa..3f54d02 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -88,6 +88,8 @@ public:
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 8e1f6bc..5d0ce3a 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 04d0605..f054e37 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -214,6 +214,8 @@ protected:
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 29cf585..7279075 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 0000000..405a73b
--- /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")