forked from OSchip/llvm-project
Added an option to "process detach" to keep the process stopped, if the process plugin (or in the
case of ProcessGDBRemote the stub we are talking to) know how to do that. rdar://problem/13680832 llvm-svn: 180831
This commit is contained in:
parent
3ca3cd1ed7
commit
a23f73dbbc
|
@ -204,6 +204,9 @@ public:
|
|||
lldb::SBError
|
||||
Detach ();
|
||||
|
||||
lldb::SBError
|
||||
Detach (bool keep_stopped);
|
||||
|
||||
lldb::SBError
|
||||
Signal (int signal);
|
||||
|
||||
|
|
|
@ -91,6 +91,12 @@ public:
|
|||
|
||||
void
|
||||
SetStopOnSharedLibraryEvents (bool stop);
|
||||
|
||||
bool
|
||||
GetDetachKeepsStopped () const;
|
||||
|
||||
void
|
||||
SetDetachKeepsStopped (bool keep_stopped);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ProcessProperties> ProcessPropertiesSP;
|
||||
|
@ -1943,11 +1949,14 @@ public:
|
|||
/// This function is not meant to be overridden by Process
|
||||
/// subclasses.
|
||||
///
|
||||
/// @param[in] keep_stopped
|
||||
/// If true, don't resume the process on detach.
|
||||
///
|
||||
/// @return
|
||||
/// Returns an error object.
|
||||
//------------------------------------------------------------------
|
||||
Error
|
||||
Detach ();
|
||||
Detach (bool keep_stopped);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Kills the process and shuts down all threads that were spawned
|
||||
|
@ -2334,7 +2343,7 @@ public:
|
|||
/// false otherwise.
|
||||
//------------------------------------------------------------------
|
||||
virtual Error
|
||||
DoDetach ()
|
||||
DoDetach (bool keep_stopped)
|
||||
{
|
||||
Error error;
|
||||
error.SetErrorStringWithFormat("error: %s does not support detaching from processes", GetShortPluginName());
|
||||
|
|
|
@ -785,13 +785,21 @@ SBProcess::Kill ()
|
|||
|
||||
SBError
|
||||
SBProcess::Detach ()
|
||||
{
|
||||
// FIXME: This should come from a process default.
|
||||
bool keep_stopped = false;
|
||||
Detach (keep_stopped);
|
||||
}
|
||||
|
||||
SBError
|
||||
SBProcess::Detach (bool keep_stopped)
|
||||
{
|
||||
SBError sb_error;
|
||||
ProcessSP process_sp(GetSP());
|
||||
if (process_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex());
|
||||
sb_error.SetError (process_sp->Detach());
|
||||
sb_error.SetError (process_sp->Detach(keep_stopped));
|
||||
}
|
||||
else
|
||||
sb_error.SetErrorString ("SBProcess is invalid");
|
||||
|
|
|
@ -75,7 +75,8 @@ protected:
|
|||
{
|
||||
if (process->GetShouldDetach())
|
||||
{
|
||||
Error detach_error (process->Detach());
|
||||
bool keep_stopped = false;
|
||||
Error detach_error (process->Detach(keep_stopped));
|
||||
if (detach_error.Success())
|
||||
{
|
||||
result.SetStatus (eReturnStatusSuccessFinishResult);
|
||||
|
@ -903,6 +904,68 @@ CommandObjectProcessContinue::CommandOptions::g_option_table[] =
|
|||
class CommandObjectProcessDetach : public CommandObjectParsed
|
||||
{
|
||||
public:
|
||||
class CommandOptions : public Options
|
||||
{
|
||||
public:
|
||||
|
||||
CommandOptions (CommandInterpreter &interpreter) :
|
||||
Options (interpreter)
|
||||
{
|
||||
OptionParsingStarting ();
|
||||
}
|
||||
|
||||
~CommandOptions ()
|
||||
{
|
||||
}
|
||||
|
||||
Error
|
||||
SetOptionValue (uint32_t option_idx, const char *option_arg)
|
||||
{
|
||||
Error error;
|
||||
const int short_option = m_getopt_table[option_idx].val;
|
||||
|
||||
switch (short_option)
|
||||
{
|
||||
case 's':
|
||||
bool tmp_result;
|
||||
bool success;
|
||||
tmp_result = Args::StringToBoolean(option_arg, false, &success);
|
||||
if (!success)
|
||||
error.SetErrorStringWithFormat("invalid boolean option: \"%s\"", option_arg);
|
||||
else
|
||||
{
|
||||
if (tmp_result)
|
||||
m_keep_stopped = eLazyBoolYes;
|
||||
else
|
||||
m_keep_stopped = eLazyBoolNo;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
|
||||
break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
OptionParsingStarting ()
|
||||
{
|
||||
m_keep_stopped = eLazyBoolCalculate;
|
||||
}
|
||||
|
||||
const OptionDefinition*
|
||||
GetDefinitions ()
|
||||
{
|
||||
return g_option_table;
|
||||
}
|
||||
|
||||
// Options table: Required for subclasses of Options.
|
||||
|
||||
static OptionDefinition g_option_table[];
|
||||
|
||||
// Instance variables to hold the values for command options.
|
||||
LazyBool m_keep_stopped;
|
||||
};
|
||||
|
||||
CommandObjectProcessDetach (CommandInterpreter &interpreter) :
|
||||
CommandObjectParsed (interpreter,
|
||||
|
@ -911,7 +974,8 @@ public:
|
|||
"process detach",
|
||||
eFlagRequiresProcess |
|
||||
eFlagTryTargetAPILock |
|
||||
eFlagProcessMustBeLaunched)
|
||||
eFlagProcessMustBeLaunched),
|
||||
m_options(interpreter)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -919,13 +983,35 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
Options *
|
||||
GetOptions ()
|
||||
{
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
bool
|
||||
DoExecute (Args& command, CommandReturnObject &result)
|
||||
{
|
||||
Process *process = m_exe_ctx.GetProcessPtr();
|
||||
result.AppendMessageWithFormat ("Detaching from process %" PRIu64 "\n", process->GetID());
|
||||
Error error (process->Detach());
|
||||
// FIXME: This will be a Command Option:
|
||||
bool keep_stopped;
|
||||
if (m_options.m_keep_stopped == eLazyBoolCalculate)
|
||||
{
|
||||
// Check the process default:
|
||||
if (process->GetDetachKeepsStopped())
|
||||
keep_stopped = true;
|
||||
else
|
||||
keep_stopped = false;
|
||||
}
|
||||
else if (m_options.m_keep_stopped == eLazyBoolYes)
|
||||
keep_stopped = true;
|
||||
else
|
||||
keep_stopped = false;
|
||||
|
||||
Error error (process->Detach(keep_stopped));
|
||||
if (error.Success())
|
||||
{
|
||||
result.SetStatus (eReturnStatusSuccessFinishResult);
|
||||
|
@ -938,6 +1024,15 @@ protected:
|
|||
}
|
||||
return result.Succeeded();
|
||||
}
|
||||
|
||||
CommandOptions m_options;
|
||||
};
|
||||
|
||||
OptionDefinition
|
||||
CommandObjectProcessDetach::CommandOptions::g_option_table[] =
|
||||
{
|
||||
{ LLDB_OPT_SET_1, false, "keep-stopped", 's', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the process should be kept stopped on detach (if possible)." },
|
||||
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
@ -295,7 +295,6 @@ CommandObject::CheckRequirements (CommandReturnObject &result)
|
|||
else
|
||||
{
|
||||
StateType state = process->GetState();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case eStateInvalid:
|
||||
|
|
|
@ -507,12 +507,12 @@ ProcessKDP::DoHalt (bool &caused_stop)
|
|||
}
|
||||
|
||||
Error
|
||||
ProcessKDP::DoDetach()
|
||||
ProcessKDP::DoDetach(bool keep_stopped)
|
||||
{
|
||||
Error error;
|
||||
Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf ("ProcessKDP::DoDetach()");
|
||||
log->Printf ("ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped);
|
||||
|
||||
if (m_comm.IsRunning())
|
||||
{
|
||||
|
@ -525,7 +525,8 @@ ProcessKDP::DoDetach()
|
|||
|
||||
m_thread_list.DiscardThreadPlans();
|
||||
|
||||
if (m_comm.IsConnected())
|
||||
// If we are going to keep the target stopped, then don't send the disconnect message.
|
||||
if (!keep_stopped && m_comm.IsConnected())
|
||||
{
|
||||
|
||||
m_comm.SendRequestDisconnect();
|
||||
|
@ -554,7 +555,8 @@ Error
|
|||
ProcessKDP::DoDestroy ()
|
||||
{
|
||||
// For KDP there really is no difference between destroy and detach
|
||||
return DoDetach();
|
||||
bool keep_stopped = false;
|
||||
return DoDetach(keep_stopped);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -135,7 +135,7 @@ public:
|
|||
DoHalt (bool &caused_stop);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDetach ();
|
||||
DoDetach (bool keep_stopped);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoSignal (int signal);
|
||||
|
|
|
@ -283,9 +283,16 @@ ProcessPOSIX::DoHalt(bool &caused_stop)
|
|||
}
|
||||
|
||||
Error
|
||||
ProcessPOSIX::DoDetach()
|
||||
ProcessPOSIX::DoDetach(bool keep_stopped)
|
||||
{
|
||||
Error error;
|
||||
if (keep_stopped)
|
||||
{
|
||||
// FIXME: If you want to implement keep_stopped on Linux,
|
||||
// this would be the place to do it.
|
||||
error.SetErrorString("Detaching with keep_stopped true is not currently supported on Linux.");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = m_monitor->Detach();
|
||||
if (error.Success())
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
DoHalt(bool &caused_stop);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDetach();
|
||||
DoDetach(bool keep_stopped);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoSignal(int signal);
|
||||
|
|
|
@ -52,6 +52,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) :
|
|||
m_supports_alloc_dealloc_memory (eLazyBoolCalculate),
|
||||
m_supports_memory_region_info (eLazyBoolCalculate),
|
||||
m_supports_watchpoint_support_info (eLazyBoolCalculate),
|
||||
m_supports_detach_stay_stopped (eLazyBoolCalculate),
|
||||
m_watchpoints_trigger_after_instruction(eLazyBoolCalculate),
|
||||
m_attach_or_wait_reply(eLazyBoolCalculate),
|
||||
m_prepare_for_reg_writing_reply (eLazyBoolCalculate),
|
||||
|
@ -1396,10 +1397,48 @@ GDBRemoteCommunicationClient::DeallocateMemory (addr_t addr)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteCommunicationClient::Detach ()
|
||||
Error
|
||||
GDBRemoteCommunicationClient::Detach (bool keep_stopped)
|
||||
{
|
||||
return SendPacket ("D", 1) > 0;
|
||||
Error error;
|
||||
|
||||
if (keep_stopped)
|
||||
{
|
||||
if (m_supports_detach_stay_stopped == eLazyBoolCalculate)
|
||||
{
|
||||
char packet[64];
|
||||
const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:");
|
||||
assert (packet_len < sizeof(packet));
|
||||
StringExtractorGDBRemote response;
|
||||
if (SendPacketAndWaitForResponse (packet, packet_len, response, false))
|
||||
{
|
||||
m_supports_detach_stay_stopped = eLazyBoolYes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_supports_detach_stay_stopped = eLazyBoolNo;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_supports_detach_stay_stopped == eLazyBoolNo)
|
||||
{
|
||||
error.SetErrorString("Stays stopped not supported by this target.");
|
||||
return error;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t num_sent = SendPacket ("D1", 2);
|
||||
if (num_sent == 0)
|
||||
error.SetErrorString ("Sending extended disconnect packet failed.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t num_sent = SendPacket ("D", 1);
|
||||
if (num_sent == 0)
|
||||
error.SetErrorString ("Sending disconnect packet failed.");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
Error
|
||||
|
|
|
@ -199,8 +199,8 @@ public:
|
|||
bool
|
||||
DeallocateMemory (lldb::addr_t addr);
|
||||
|
||||
bool
|
||||
Detach ();
|
||||
lldb_private::Error
|
||||
Detach (bool keep_stopped);
|
||||
|
||||
lldb_private::Error
|
||||
GetMemoryRegionInfo (lldb::addr_t addr,
|
||||
|
@ -381,6 +381,7 @@ protected:
|
|||
lldb_private::LazyBool m_supports_alloc_dealloc_memory;
|
||||
lldb_private::LazyBool m_supports_memory_region_info;
|
||||
lldb_private::LazyBool m_supports_watchpoint_support_info;
|
||||
lldb_private::LazyBool m_supports_detach_stay_stopped;
|
||||
lldb_private::LazyBool m_watchpoints_trigger_after_instruction;
|
||||
lldb_private::LazyBool m_attach_or_wait_reply;
|
||||
lldb_private::LazyBool m_prepare_for_reg_writing_reply;
|
||||
|
|
|
@ -1703,25 +1703,29 @@ ProcessGDBRemote::DoHalt (bool &caused_stop)
|
|||
}
|
||||
|
||||
Error
|
||||
ProcessGDBRemote::DoDetach()
|
||||
ProcessGDBRemote::DoDetach(bool keep_stopped)
|
||||
{
|
||||
Error error;
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf ("ProcessGDBRemote::DoDetach()");
|
||||
|
||||
log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped);
|
||||
|
||||
DisableAllBreakpointSites ();
|
||||
|
||||
m_thread_list.DiscardThreadPlans();
|
||||
|
||||
bool success = m_gdb_comm.Detach ();
|
||||
error = m_gdb_comm.Detach (keep_stopped);
|
||||
if (log)
|
||||
{
|
||||
if (success)
|
||||
if (error.Success())
|
||||
log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully");
|
||||
else
|
||||
log->PutCString ("ProcessGDBRemote::DoDetach() detach packet send failed");
|
||||
log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : "<unknown error>");
|
||||
}
|
||||
|
||||
if (!error.Success())
|
||||
return error;
|
||||
|
||||
// Sleep for one second to let the process get all detached...
|
||||
StopAsyncThread ();
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ public:
|
|||
DoHalt (bool &caused_stop);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDetach ();
|
||||
DoDetach (bool keep_stopped);
|
||||
|
||||
virtual bool
|
||||
DetachRequiresHalt() { return true; }
|
||||
|
|
|
@ -101,6 +101,7 @@ g_properties[] =
|
|||
{ "unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, errors in expression evaluation will unwind the stack back to the state before the call." },
|
||||
{ "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." },
|
||||
{ "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." },
|
||||
{ "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." },
|
||||
{ NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -110,7 +111,8 @@ enum {
|
|||
ePropertyIgnoreBreakpointsInExpressions,
|
||||
ePropertyUnwindOnErrorInExpressions,
|
||||
ePropertyPythonOSPluginPath,
|
||||
ePropertyStopOnSharedLibraryEvents
|
||||
ePropertyStopOnSharedLibraryEvents,
|
||||
ePropertyDetachKeepsStopped
|
||||
};
|
||||
|
||||
ProcessProperties::ProcessProperties (bool is_global) :
|
||||
|
@ -213,6 +215,20 @@ ProcessProperties::SetStopOnSharedLibraryEvents (bool stop)
|
|||
m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop);
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessProperties::GetDetachKeepsStopped () const
|
||||
{
|
||||
const uint32_t idx = ePropertyDetachKeepsStopped;
|
||||
return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessProperties::SetDetachKeepsStopped (bool stop)
|
||||
{
|
||||
const uint32_t idx = ePropertyDetachKeepsStopped;
|
||||
m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessInstanceInfo::Dump (Stream &s, Platform *platform) const
|
||||
{
|
||||
|
@ -1096,7 +1112,11 @@ Process::Finalize()
|
|||
case eStateCrashed:
|
||||
case eStateSuspended:
|
||||
if (GetShouldDetach())
|
||||
Detach();
|
||||
{
|
||||
// FIXME: This will have to be a process setting:
|
||||
bool keep_stopped = false;
|
||||
Detach(keep_stopped);
|
||||
}
|
||||
else
|
||||
Destroy();
|
||||
break;
|
||||
|
@ -3431,7 +3451,7 @@ Process::HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp)
|
|||
}
|
||||
|
||||
Error
|
||||
Process::Detach ()
|
||||
Process::Detach (bool keep_stopped)
|
||||
{
|
||||
EventSP exit_event_sp;
|
||||
Error error;
|
||||
|
@ -3458,12 +3478,16 @@ Process::Detach ()
|
|||
}
|
||||
}
|
||||
|
||||
error = DoDetach();
|
||||
error = DoDetach(keep_stopped);
|
||||
if (error.Success())
|
||||
{
|
||||
DidDetach();
|
||||
StopPrivateStateThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
return error;
|
||||
}
|
||||
}
|
||||
m_destroy_in_process = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue