const char *
GetLaunchEventData () const;
+ bool
+ GetDetachOnError() const;
+
+ void
+ SetDetachOnError(bool enable);
+
protected:
friend class SBTarget;
return m_event_data.c_str();
}
+ void
+ SetDetachOnError (bool enable)
+ {
+ if (enable)
+ m_flags.Set(lldb::eLaunchFlagDetachOnError);
+ else
+ m_flags.Clear(lldb::eLaunchFlagDetachOnError);
+ }
+
+ bool
+ GetDetachOnError () const
+ {
+ return m_flags.Test(lldb::eLaunchFlagDetachOnError);
+ }
+
protected:
std::string m_working_dir;
std::string m_plugin_name;
};
//----------------------------------------------------------------------
-// ProcessLaunchInfo
+// ProcessAttachInfo
//
-// Describes any information that is required to launch a process.
+// Describes any information that is required to attach to a process.
//----------------------------------------------------------------------
class ProcessAttachInfo : public ProcessInstanceInfo
m_resume_count (0),
m_wait_for_launch (false),
m_ignore_existing (true),
- m_continue_once_attached (false)
+ m_continue_once_attached (false),
+ m_detach_on_error (true)
{
}
m_resume_count (0),
m_wait_for_launch (false),
m_ignore_existing (true),
- m_continue_once_attached (false)
+ m_continue_once_attached (false),
+ m_detach_on_error(true)
{
ProcessInfo::operator= (launch_info);
SetProcessPluginName (launch_info.GetProcessPluginName());
SetResumeCount (launch_info.GetResumeCount());
SetHijackListener(launch_info.GetHijackListener());
+ m_detach_on_error = launch_info.GetDetachOnError();
}
bool
m_hijack_listener_sp = listener_sp;
}
-
+ bool
+ GetDetachOnError () const
+ {
+ return m_detach_on_error;
+ }
+
+ void
+ SetDetachOnError (bool enable)
+ {
+ m_detach_on_error = enable;
+ }
+
protected:
lldb::ListenerSP m_hijack_listener_sp;
std::string m_plugin_name;
bool m_wait_for_launch;
bool m_ignore_existing;
bool m_continue_once_attached; // Supports the use-case scenario of immediately continuing the process once attached.
+ bool m_detach_on_error; // If we are debugging remotely, instruct the stub to detach rather than killing the target on error.
};
class ProcessLaunchCommandOptions : public Options
SetDisableASLR (bool b);
bool
+ GetDetachOnError () const;
+
+ void
+ SetDetachOnError (bool b);
+
+ bool
GetDisableSTDIO () const;
void
typedef enum LaunchFlags
{
eLaunchFlagNone = 0u,
- eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process
- eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged
- eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point
- eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization
- eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app)
- eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host
- eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion
+ eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process
+ eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged
+ eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point
+ eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization
+ eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app)
+ eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host
+ eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion
eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7), ///< Launch the process in a separate process group
- eLaunchFlagsDontSetExitStatus = (1u << 8) ///< If you are going to hand the process off (e.g. to debugserver)
- ///< set this flag so lldb & the handee don't race to set its exit status.
+ eLaunchFlagDontSetExitStatus = (1u << 8), ///< If you are going to hand the process off (e.g. to debugserver)
+ ///< set this flag so lldb & the handee don't race to set its exit status.
+ eLaunchFlagDetachOnError = (1u << 9) ///< If set, then the client stub should detach rather than killing the debugee
+ ///< if it loses connection with lldb.
} LaunchFlags;
//----------------------------------------------------------------------
const char *
GetLaunchEventData () const;
+ bool
+ GetDetachOnError() const;
+
+ void
+ SetDetachOnError(bool enable);
+
};
class SBAttachInfo
return m_opaque_sp->GetLaunchEventData ();
}
+void
+SBLaunchInfo::SetDetachOnError (bool enable)
+{
+ m_opaque_sp->SetDetachOnError (enable);
+}
+
+bool
+SBLaunchInfo::GetDetachOnError () const
+{
+ return m_opaque_sp->GetDetachOnError ();
+}
+
SBAttachInfo::SBAttachInfo () :
m_opaque_sp (new ProcessAttachInfo())
{
if (target->GetDisableASLR())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
+ if (target->GetDetachOnError())
+ m_options.launch_info.GetFlags().Set (eLaunchFlagDetachOnError);
+
if (target->GetDisableSTDIO())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO);
const bool monitor_signals = false;
Host::MonitorChildProcessCallback callback = nullptr;
- if (!launch_info.GetFlags().Test(lldb::eLaunchFlagsDontSetExitStatus))
+ if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus))
callback = Process::SetProcessExitStatus;
StartMonitoringChildProcess (callback,
// We are going to hand this process off to debugserver which will be in charge of setting the exit status.
// We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a
// race between debugserver & us for who will find out about the debugged process's death.
- launch_info.GetFlags().Set(eLaunchFlagsDontSetExitStatus);
+ launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus);
process_sp = Platform::DebugProcess (launch_info, debugger, target, listener, error);
}
else
m_gdb_client.SetSTDOUT ("/dev/null");
m_gdb_client.SetSTDERR ("/dev/null");
m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR));
+ m_gdb_client.SetDetachOnError (launch_info.GetFlags().Test (eLaunchFlagDetachOnError));
const char *working_dir = launch_info.GetWorkingDirectory();
if (working_dir && working_dir[0])
return -1;
}
+int
+GDBRemoteCommunicationClient::SetDetachOnError (bool enable)
+{
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDetachOnError:%i", enable ? 1 : 0);
+ assert (packet_len < (int)sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success)
+ {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+
bool
GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info)
{
/// be launched with the 'A' packet.
///
/// @param[in] enable
- /// A boolean value indicating wether to disable ASLR or not.
+ /// A boolean value indicating whether to disable ASLR or not.
///
/// @return
/// Zero if the for success, or an error code for failure.
//------------------------------------------------------------------
int
SetDisableASLR (bool enable);
+
+ //------------------------------------------------------------------
+ /// Sets the DetachOnError flag to \a enable for the process controlled by the stub.
+ ///
+ /// @param[in] enable
+ /// A boolean value indicating whether to detach on error or not.
+ ///
+ /// @return
+ /// Zero if the for success, or an error code for failure.
+ //------------------------------------------------------------------
+ int
+ SetDetachOnError (bool enable);
//------------------------------------------------------------------
/// Sets the working directory to \a path for a process that will
packet_result = Handle_QSetDisableASLR (packet);
break;
+ case StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError:
+ packet_result = Handle_QSetDetachOnError (packet);
+ break;
+
case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN:
packet_result = Handle_QSetSTDIN (packet);
break;
}
GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::Handle_QSetDetachOnError (StringExtractorGDBRemote &packet)
+{
+ packet.SetFilePos(::strlen ("QSetDetachOnError:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set (eLaunchFlagDetachOnError);
+ else
+ m_process_launch_info.GetFlags().Clear (eLaunchFlagDetachOnError);
+ return SendOKResponse ();
+}
+
+GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("QSetWorkingDir:"));
Handle_QSetDisableASLR (StringExtractorGDBRemote &packet);
PacketResult
+ Handle_QSetDetachOnError (StringExtractorGDBRemote &packet);
+
+ PacketResult
Handle_QSetWorkingDir (StringExtractorGDBRemote &packet);
PacketResult
m_gdb_comm.SetSTDERR (stderr_path);
m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR);
+ m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError);
m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName());
if (error.Success())
{
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
char packet[64];
const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid);
SetID (attach_pid);
{
StreamString packet;
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
if (attach_info.GetWaitForLaunch())
{
if (!m_gdb_comm.GetVAttachOrWaitSupported())
{ "input-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for reading its standard input." },
{ "output-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard output." },
{ "error-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard error." },
+ { "detach-on-error" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "debugserver will detach (rather than killing) a process if it loses connection with lldb." },
{ "disable-aslr" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Disable Address Space Layout Randomization (ASLR)" },
{ "disable-stdio" , OptionValue::eTypeBoolean , false, false , NULL, NULL, "Disable stdin/stdout for process (e.g. for a GUI application)" },
{ "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsHeaders , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. "
ePropertyInputPath,
ePropertyOutputPath,
ePropertyErrorPath,
+ ePropertyDetachOnError,
ePropertyDisableASLR,
ePropertyDisableSTDIO,
ePropertyInlineStrategy,
}
bool
+TargetProperties::GetDetachOnError () const
+{
+ const uint32_t idx = ePropertyDetachOnError;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+TargetProperties::SetDetachOnError (bool b)
+{
+ const uint32_t idx = ePropertyDetachOnError;
+ m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
+}
+
+bool
TargetProperties::GetDisableSTDIO () const
{
const uint32_t idx = ePropertyDisableSTDIO;
if (PACKET_MATCHES ("QStartNoAckMode")) return eServerPacketType_QStartNoAckMode;
if (PACKET_STARTS_WITH ("QSaveRegisterState")) return eServerPacketType_QSaveRegisterState;
if (PACKET_STARTS_WITH ("QSetDisableASLR:")) return eServerPacketType_QSetDisableASLR;
+ if (PACKET_STARTS_WITH ("QSetDetachOnError:")) return eServerPacketType_QSetDetachOnError;
if (PACKET_STARTS_WITH ("QSetSTDIN:")) return eServerPacketType_QSetSTDIN;
if (PACKET_STARTS_WITH ("QSetSTDOUT:")) return eServerPacketType_QSetSTDOUT;
if (PACKET_STARTS_WITH ("QSetSTDERR:")) return eServerPacketType_QSetSTDERR;
eServerPacketType_QEnvironment,
eServerPacketType_QLaunchArch,
eServerPacketType_QSetDisableASLR,
+ eServerPacketType_QSetDetachOnError,
eServerPacketType_QSetSTDIN,
eServerPacketType_QSetSTDOUT,
eServerPacketType_QSetSTDERR,
t.push_back (Packet (set_enable_profiling, &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target."));
t.push_back (Packet (watchpoint_support_info, &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints"));
t.push_back (Packet (set_process_event, &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed to the process, can be set before the process is started, or after."));
+ t.push_back (Packet (set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb."));
t.push_back (Packet (speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:", "Test the maximum speed at which packet can be sent/received."));
}
}
rnb_err_t
+RNBRemote::HandlePacket_QSetDetachOnError (const char *p)
+{
+ p += sizeof ("QSetDetachOnError:") - 1;
+ bool should_detach = true;
+ switch (*p)
+ {
+ case '0': should_detach = false; break;
+ case '1': should_detach = true; break;
+ default:
+ return HandlePacket_ILLFORMED (__FILE__, __LINE__, p, "Invalid value for QSetDetachOnError - should be 0 or 1");
+ break;
+ }
+
+ m_ctx.SetDetachOnError(should_detach);
+ return SendPacket ("OK");
+}
+
+rnb_err_t
RNBRemote::HandlePacket_QListThreadsInStopReply (const char *p)
{
// If this packet is received, it allows us to send an extra key/value
save_register_state, // '_g'
restore_register_state, // '_G'
speed_test, // 'qSpeedTest:'
+ set_detach_on_error, // 'QSetDetachOnError:'
unknown_type
} PacketEnum;
rnb_err_t HandlePacket_WatchpointSupportInfo (const char *p);
rnb_err_t HandlePacket_qSpeedTest (const char *p);
rnb_err_t HandlePacket_stop_process (const char *p);
+ rnb_err_t HandlePacket_QSetDetachOnError (const char *p);
rnb_err_t SendStopReplyPacketForThread (nub_thread_t tid);
rnb_err_t SendHexEncodedBytePacket (const char *header, const void *buf, size_t buf_len, const char *footer);