Added an option to turn OFF the "detach on error" behavior that was added

to debugserver when launching processes.

<rdar://problem/16216199>

llvm-svn: 211658
This commit is contained in:
Jim Ingham 2014-06-25 02:32:56 +00:00
parent ab8d0a0dd5
commit 106d02866d
20 changed files with 176 additions and 17 deletions

View File

@ -145,6 +145,12 @@ public:
const char *
GetLaunchEventData () const;
bool
GetDetachOnError() const;
void
SetDetachOnError(bool enable);
protected:
friend class SBTarget;

View File

@ -866,6 +866,21 @@ public:
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;
@ -882,9 +897,9 @@ protected:
};
//----------------------------------------------------------------------
// 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
@ -896,7 +911,8 @@ public:
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)
{
}
@ -906,12 +922,14 @@ public:
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
@ -1014,7 +1032,18 @@ public:
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;
@ -1022,6 +1051,7 @@ protected:
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

View File

@ -81,6 +81,12 @@ public:
void
SetDisableASLR (bool b);
bool
GetDetachOnError () const;
void
SetDetachOnError (bool b);
bool
GetDisableSTDIO () const;

View File

@ -39,16 +39,18 @@ namespace lldb {
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;
//----------------------------------------------------------------------

View File

@ -107,6 +107,12 @@ public:
const char *
GetLaunchEventData () const;
bool
GetDetachOnError() const;
void
SetDetachOnError(bool enable);
};
class SBAttachInfo

View File

@ -292,6 +292,18 @@ SBLaunchInfo::GetLaunchEventData () const
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())
{

View File

@ -208,6 +208,9 @@ protected:
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);

View File

@ -1669,7 +1669,7 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
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,

View File

@ -788,7 +788,7 @@ PlatformDarwin::DebugProcess (ProcessLaunchInfo &launch_info,
// 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

View File

@ -353,6 +353,7 @@ PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
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])

View File

@ -2256,6 +2256,25 @@ GDBRemoteCommunicationClient::SetDisableASLR (bool enable)
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)
{

View File

@ -205,13 +205,25 @@ public:
/// 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

View File

@ -202,6 +202,10 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
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;
@ -1139,6 +1143,17 @@ GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote &
return SendOKResponse ();
}
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)
{

View File

@ -264,6 +264,9 @@ protected:
PacketResult
Handle_QSetDisableASLR (StringExtractorGDBRemote &packet);
PacketResult
Handle_QSetDetachOnError (StringExtractorGDBRemote &packet);
PacketResult
Handle_QSetWorkingDir (StringExtractorGDBRemote &packet);

View File

@ -799,6 +799,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info)
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());
@ -1071,6 +1072,8 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const Process
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);
@ -1108,6 +1111,8 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro
{
StreamString packet;
m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
if (attach_info.GetWaitForLaunch())
{
if (!m_gdb_comm.GetVAttachOrWaitSupported())

View File

@ -2642,6 +2642,7 @@ g_properties[] =
{ "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. "
@ -2688,6 +2689,7 @@ enum
ePropertyInputPath,
ePropertyOutputPath,
ePropertyErrorPath,
ePropertyDetachOnError,
ePropertyDisableASLR,
ePropertyDisableSTDIO,
ePropertyInlineStrategy,
@ -2870,6 +2872,20 @@ TargetProperties::SetDisableASLR (bool b)
m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
}
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
{

View File

@ -92,6 +92,7 @@ StringExtractorGDBRemote::GetServerPacketType () const
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;

View File

@ -62,6 +62,7 @@ public:
eServerPacketType_QEnvironment,
eServerPacketType_QLaunchArch,
eServerPacketType_QSetDisableASLR,
eServerPacketType_QSetDetachOnError,
eServerPacketType_QSetSTDIN,
eServerPacketType_QSetSTDOUT,
eServerPacketType_QSetSTDERR,

View File

@ -203,6 +203,7 @@ RNBRemote::CreatePacketTable ()
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."));
}
@ -2017,6 +2018,24 @@ RNBRemote::HandlePacket_QSyncThreadState (const char *p)
return SendPacket ("E61");
}
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)
{

View File

@ -126,6 +126,7 @@ public:
save_register_state, // '_g'
restore_register_state, // '_G'
speed_test, // 'qSpeedTest:'
set_detach_on_error, // 'QSetDetachOnError:'
unknown_type
} PacketEnum;
@ -234,6 +235,7 @@ public:
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);