forked from OSchip/llvm-project
[lldb] Support SaveCore() from gdb-remote client
Extend PluginManager::SaveCore() to support saving core dumps via Process plugins. Implement the client-side part of qSaveCore request in the gdb-remote plugin, that creates the core dump on the remote host and then uses vFile packets to transfer it. Differential Revision: https://reviews.llvm.org/D101329
This commit is contained in:
parent
3322354bfc
commit
25fbbc5936
|
@ -687,6 +687,18 @@ public:
|
||||||
"Not implemented");
|
"Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save core dump into the specified file.
|
||||||
|
///
|
||||||
|
/// \param[in] outfile
|
||||||
|
/// Path to store core dump in.
|
||||||
|
///
|
||||||
|
/// \return
|
||||||
|
/// true if saved successfully, false if saving the core dump
|
||||||
|
/// is not supported by the plugin, error otherwise.
|
||||||
|
virtual llvm::Expected<bool> SaveCore(llvm::StringRef outfile) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual JITLoaderList &GetJITLoaders();
|
virtual JITLoaderList &GetJITLoaders();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "lldb/Host/FileSystem.h"
|
#include "lldb/Host/FileSystem.h"
|
||||||
#include "lldb/Host/HostInfo.h"
|
#include "lldb/Host/HostInfo.h"
|
||||||
#include "lldb/Interpreter/OptionValueProperties.h"
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
||||||
|
#include "lldb/Target/Process.h"
|
||||||
#include "lldb/Utility/ConstString.h"
|
#include "lldb/Utility/ConstString.h"
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/Status.h"
|
#include "lldb/Utility/Status.h"
|
||||||
|
@ -687,6 +688,16 @@ Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
|
||||||
const FileSpec &outfile,
|
const FileSpec &outfile,
|
||||||
lldb::SaveCoreStyle &core_style,
|
lldb::SaveCoreStyle &core_style,
|
||||||
const ConstString plugin_name) {
|
const ConstString plugin_name) {
|
||||||
|
if (!plugin_name) {
|
||||||
|
// Try saving core directly from the process plugin first.
|
||||||
|
llvm::Expected<bool> ret = process_sp->SaveCore(outfile.GetPath());
|
||||||
|
if (!ret)
|
||||||
|
return Status(std::move(ret.takeError()));
|
||||||
|
if (ret.get())
|
||||||
|
return Status();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to object plugins.
|
||||||
Status error;
|
Status error;
|
||||||
auto &instances = GetObjectFileInstances().GetInstances();
|
auto &instances = GetObjectFileInstances().GetInstances();
|
||||||
for (auto &instance : instances) {
|
for (auto &instance : instances) {
|
||||||
|
|
|
@ -257,6 +257,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
|
||||||
m_attach_or_wait_reply = eLazyBoolCalculate;
|
m_attach_or_wait_reply = eLazyBoolCalculate;
|
||||||
m_avoid_g_packets = eLazyBoolCalculate;
|
m_avoid_g_packets = eLazyBoolCalculate;
|
||||||
m_supports_multiprocess = eLazyBoolCalculate;
|
m_supports_multiprocess = eLazyBoolCalculate;
|
||||||
|
m_supports_qSaveCore = eLazyBoolCalculate;
|
||||||
m_supports_qXfer_auxv_read = eLazyBoolCalculate;
|
m_supports_qXfer_auxv_read = eLazyBoolCalculate;
|
||||||
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
|
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
|
||||||
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
|
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
|
||||||
|
@ -312,6 +313,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
||||||
m_supports_qEcho = eLazyBoolNo;
|
m_supports_qEcho = eLazyBoolNo;
|
||||||
m_supports_QPassSignals = eLazyBoolNo;
|
m_supports_QPassSignals = eLazyBoolNo;
|
||||||
m_supports_memory_tagging = eLazyBoolNo;
|
m_supports_memory_tagging = eLazyBoolNo;
|
||||||
|
m_supports_qSaveCore = eLazyBoolNo;
|
||||||
|
|
||||||
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
|
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
|
||||||
// not, we assume no limit
|
// not, we assume no limit
|
||||||
|
@ -359,6 +361,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
||||||
m_supports_multiprocess = eLazyBoolYes;
|
m_supports_multiprocess = eLazyBoolYes;
|
||||||
else if (x == "memory-tagging+")
|
else if (x == "memory-tagging+")
|
||||||
m_supports_memory_tagging = eLazyBoolYes;
|
m_supports_memory_tagging = eLazyBoolYes;
|
||||||
|
else if (x == "qSaveCore+")
|
||||||
|
m_supports_qSaveCore = eLazyBoolYes;
|
||||||
// Look for a list of compressions in the features list e.g.
|
// Look for a list of compressions in the features list e.g.
|
||||||
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
|
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
|
||||||
// deflate,lzma
|
// deflate,lzma
|
||||||
|
@ -501,6 +505,10 @@ LazyBool GDBRemoteCommunicationClient::GetThreadPacketSupported(
|
||||||
return eLazyBoolNo;
|
return eLazyBoolNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GDBRemoteCommunicationClient::GetSaveCoreSupported() const {
|
||||||
|
return m_supports_qSaveCore == eLazyBoolYes;
|
||||||
|
}
|
||||||
|
|
||||||
StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
|
StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
|
||||||
// Get information on all threads at one using the "jThreadsInfo" packet
|
// Get information on all threads at one using the "jThreadsInfo" packet
|
||||||
StructuredData::ObjectSP object_sp;
|
StructuredData::ObjectSP object_sp;
|
||||||
|
|
|
@ -547,6 +547,8 @@ public:
|
||||||
SendTraceGetBinaryData(const TraceGetBinaryDataRequest &request,
|
SendTraceGetBinaryData(const TraceGetBinaryDataRequest &request,
|
||||||
std::chrono::seconds interrupt_timeout);
|
std::chrono::seconds interrupt_timeout);
|
||||||
|
|
||||||
|
bool GetSaveCoreSupported() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
|
LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
|
||||||
LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
|
LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
|
||||||
|
@ -585,6 +587,7 @@ protected:
|
||||||
LazyBool m_supports_error_string_reply = eLazyBoolCalculate;
|
LazyBool m_supports_error_string_reply = eLazyBoolCalculate;
|
||||||
LazyBool m_supports_multiprocess = eLazyBoolCalculate;
|
LazyBool m_supports_multiprocess = eLazyBoolCalculate;
|
||||||
LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
|
LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
|
||||||
|
LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
|
||||||
|
|
||||||
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
|
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
|
||||||
m_supports_qUserName : 1, m_supports_qGroupName : 1,
|
m_supports_qUserName : 1, m_supports_qGroupName : 1,
|
||||||
|
|
|
@ -5147,6 +5147,58 @@ void ProcessGDBRemote::HandleStopReply() {
|
||||||
BuildDynamicRegisterInfo(true);
|
BuildDynamicRegisterInfo(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Expected<bool> ProcessGDBRemote::SaveCore(llvm::StringRef outfile) {
|
||||||
|
if (!m_gdb_comm.GetSaveCoreSupported())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StreamString packet;
|
||||||
|
packet.PutCString("qSaveCore;path-hint:");
|
||||||
|
packet.PutStringAsRawHex8(outfile);
|
||||||
|
|
||||||
|
StringExtractorGDBRemote response;
|
||||||
|
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) ==
|
||||||
|
GDBRemoteCommunication::PacketResult::Success) {
|
||||||
|
// TODO: grab error message from the packet? StringExtractor seems to
|
||||||
|
// be missing a method for that
|
||||||
|
if (response.IsErrorResponse())
|
||||||
|
return llvm::createStringError(
|
||||||
|
llvm::inconvertibleErrorCode(),
|
||||||
|
llvm::formatv("qSaveCore returned an error"));
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
// process the response
|
||||||
|
llvm::SmallVector<llvm::StringRef, 1> reply_data;
|
||||||
|
response.GetStringRef().split(reply_data, ';');
|
||||||
|
for (auto x : reply_data) {
|
||||||
|
if (x.consume_front("core-path:"))
|
||||||
|
StringExtractor(x).GetHexByteString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that we've gotten what we need
|
||||||
|
if (path.empty())
|
||||||
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||||
|
"qSaveCore returned no core path");
|
||||||
|
|
||||||
|
// now transfer the core file
|
||||||
|
FileSpec remote_core{llvm::StringRef(path)};
|
||||||
|
Platform &platform = *GetTarget().GetPlatform();
|
||||||
|
Status error = platform.GetFile(remote_core, FileSpec(outfile));
|
||||||
|
|
||||||
|
if (platform.IsRemote()) {
|
||||||
|
// NB: we unlink the file on error too
|
||||||
|
platform.Unlink(remote_core);
|
||||||
|
if (error.Fail())
|
||||||
|
return error.ToError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||||
|
"Unable to send qSaveCore");
|
||||||
|
}
|
||||||
|
|
||||||
static const char *const s_async_json_packet_prefix = "JSON-async:";
|
static const char *const s_async_json_packet_prefix = "JSON-async:";
|
||||||
|
|
||||||
static StructuredData::ObjectSP
|
static StructuredData::ObjectSP
|
||||||
|
|
|
@ -235,6 +235,8 @@ public:
|
||||||
void DidVForkDone() override;
|
void DidVForkDone() override;
|
||||||
void DidExec() override;
|
void DidExec() override;
|
||||||
|
|
||||||
|
llvm::Expected<bool> SaveCore(llvm::StringRef outfile) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class ThreadGDBRemote;
|
friend class ThreadGDBRemote;
|
||||||
friend class GDBRemoteCommunicationClient;
|
friend class GDBRemoteCommunicationClient;
|
||||||
|
|
|
@ -63,3 +63,31 @@ class ProcessSaveCoreTestCase(TestBase):
|
||||||
self.assertTrue(self.dbg.DeleteTarget(target))
|
self.assertTrue(self.dbg.DeleteTarget(target))
|
||||||
if (os.path.isfile(core)):
|
if (os.path.isfile(core)):
|
||||||
os.unlink(core)
|
os.unlink(core)
|
||||||
|
|
||||||
|
@skipUnlessPlatform(["netbsd"])
|
||||||
|
def test_save_core_via_process_plugin(self):
|
||||||
|
self.build()
|
||||||
|
exe = self.getBuildArtifact("a.out")
|
||||||
|
core = self.getBuildArtifact("a.out.core")
|
||||||
|
try:
|
||||||
|
target = self.dbg.CreateTarget(exe)
|
||||||
|
breakpoint = target.BreakpointCreateByName("bar")
|
||||||
|
process = target.LaunchSimple(
|
||||||
|
None, None, self.get_process_working_directory())
|
||||||
|
self.assertEqual(process.GetState(), lldb.eStateStopped)
|
||||||
|
self.assertTrue(process.SaveCore(core))
|
||||||
|
self.assertTrue(os.path.isfile(core))
|
||||||
|
self.assertTrue(process.Kill().Success())
|
||||||
|
pid = process.GetProcessID()
|
||||||
|
|
||||||
|
target = self.dbg.CreateTarget(None)
|
||||||
|
process = target.LoadCore(core)
|
||||||
|
self.assertTrue(process, PROCESS_IS_VALID)
|
||||||
|
self.assertEqual(process.GetProcessID(), pid)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.assertTrue(self.dbg.DeleteTarget(target))
|
||||||
|
try:
|
||||||
|
os.unlink(core)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in New Issue