forked from OSchip/llvm-project
[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
This commit is contained in:
parent
fae0dfa642
commit
37cbd817d3
|
@ -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;
|
||||
|
|
|
@ -170,6 +170,8 @@ public:
|
|||
|
||||
eServerPacketType_qMemTags, // read memory tags
|
||||
eServerPacketType_QMemTags, // write memory tags
|
||||
|
||||
eServerPacketType_qLLDBSaveCore,
|
||||
};
|
||||
|
||||
ServerPacketType GetServerPacketType() const;
|
||||
|
|
|
@ -860,6 +860,7 @@ class GdbRemoteTestCaseBase(Base):
|
|||
"fork-events",
|
||||
"vfork-events",
|
||||
"memory-tagging",
|
||||
"qSaveCore",
|
||||
]
|
||||
|
||||
def parse_qSupported_response(self, context):
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue