From a23f73dbbcbc6008162767d64bb019389e6a1bd7 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Tue, 30 Apr 2013 23:46:08 +0000 Subject: [PATCH] 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 --- lldb/include/lldb/API/SBProcess.h | 3 + lldb/include/lldb/Target/Process.h | 13 ++- lldb/source/API/SBProcess.cpp | 10 +- lldb/source/Commands/CommandObjectProcess.cpp | 101 +++++++++++++++++- lldb/source/Interpreter/CommandObject.cpp | 1 - .../Process/MacOSX-Kernel/ProcessKDP.cpp | 10 +- .../Process/MacOSX-Kernel/ProcessKDP.h | 2 +- .../Plugins/Process/POSIX/ProcessPOSIX.cpp | 9 +- .../Plugins/Process/POSIX/ProcessPOSIX.h | 2 +- .../GDBRemoteCommunicationClient.cpp | 45 +++++++- .../gdb-remote/GDBRemoteCommunicationClient.h | 5 +- .../Process/gdb-remote/ProcessGDBRemote.cpp | 16 +-- .../Process/gdb-remote/ProcessGDBRemote.h | 2 +- lldb/source/Target/Process.cpp | 32 +++++- 14 files changed, 221 insertions(+), 30 deletions(-) diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h index 73ef89d207dd..288d7d0290d1 100644 --- a/lldb/include/lldb/API/SBProcess.h +++ b/lldb/include/lldb/API/SBProcess.h @@ -204,6 +204,9 @@ public: lldb::SBError Detach (); + lldb::SBError + Detach (bool keep_stopped); + lldb::SBError Signal (int signal); diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 82e668fe732e..090b7593444d 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -91,6 +91,12 @@ public: void SetStopOnSharedLibraryEvents (bool stop); + + bool + GetDetachKeepsStopped () const; + + void + SetDetachKeepsStopped (bool keep_stopped); }; typedef std::shared_ptr 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()); diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index ba45ce567d1c..f04fed228435 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -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"); diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 2221514d9022..7ad5f7be9351 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -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 } }; //------------------------------------------------------------------------- diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index d2b2ce548758..68756050c6f7 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -295,7 +295,6 @@ CommandObject::CheckRequirements (CommandReturnObject &result) else { StateType state = process->GetState(); - switch (state) { case eStateInvalid: diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp index 9e4cc568af59..f9b1055644b7 100644 --- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -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); } //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h index fba221ea5ed8..9de262322ae2 100644 --- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -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); diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp index 137fa46eddc6..35c365f75c03 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -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()) diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h index b16da3aca742..86c4ef33b3d7 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -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); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 9351b93761bb..6657c2ca27b9 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -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 diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 25b697de8ef5..4075eebddec5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -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; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 35e455d5f243..aa27ae8257e2 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -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() : ""); } + + if (!error.Success()) + return error; + // Sleep for one second to let the process get all detached... StopAsyncThread (); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index dc4ec561b031..5cbb13f49146 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -141,7 +141,7 @@ public: DoHalt (bool &caused_stop); virtual lldb_private::Error - DoDetach (); + DoDetach (bool keep_stopped); virtual bool DetachRequiresHalt() { return true; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index f60adca741e7..e0ea32a021ef 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -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;