diff options
Diffstat (limited to 'source/Target/Process.cpp')
-rw-r--r-- | source/Target/Process.cpp | 570 |
1 files changed, 368 insertions, 202 deletions
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index f7a26f676aba..41942829ca55 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -7,22 +7,19 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Target/Process.h" - -#include "lldb/lldb-private-log.h" - #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Event.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" @@ -30,11 +27,15 @@ #include "lldb/Host/Terminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/JITLoader.h" +#include "lldb/Target/JITLoaderList.h" #include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/CPPLanguageRuntime.h" @@ -48,7 +49,8 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" -#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/NameMatches.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" using namespace lldb; @@ -430,7 +432,7 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'i': // STDIN for read only { FileAction action; - if (action.Open (STDIN_FILENO, option_arg, true, false)) + if (action.Open(STDIN_FILENO, FileSpec{option_arg, false}, true, false)) launch_info.AppendFileAction (action); break; } @@ -438,7 +440,7 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'o': // Open STDOUT for write only { FileAction action; - if (action.Open (STDOUT_FILENO, option_arg, false, true)) + if (action.Open(STDOUT_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction (action); break; } @@ -446,7 +448,7 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'e': // STDERR for write only { FileAction action; - if (action.Open (STDERR_FILENO, option_arg, false, true)) + if (action.Open(STDERR_FILENO, FileSpec{option_arg, false}, false, true)) launch_info.AppendFileAction (action); break; } @@ -458,17 +460,18 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'n': // Disable STDIO { FileAction action; - if (action.Open (STDIN_FILENO, "/dev/null", true, false)) + const FileSpec dev_null{"/dev/null", false}; + if (action.Open(STDIN_FILENO, dev_null, true, false)) launch_info.AppendFileAction (action); - if (action.Open (STDOUT_FILENO, "/dev/null", false, true)) + if (action.Open(STDOUT_FILENO, dev_null, false, true)) launch_info.AppendFileAction (action); - if (action.Open (STDERR_FILENO, "/dev/null", false, true)) + if (action.Open(STDERR_FILENO, dev_null, false, true)) launch_info.AppendFileAction (action); break; } case 'w': - launch_info.SetWorkingDirectory (option_arg); + launch_info.SetWorkingDirectory(FileSpec{option_arg, false}); break; case 't': // Open process in new terminal window @@ -491,6 +494,17 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op break; } + case 'X': // shell expand args. + { + bool success; + const bool expand_args = Args::StringToBoolean (option_arg, true, &success); + if (success) + launch_info.SetShellExpandArguments(expand_args); + else + error.SetErrorStringWithFormat ("Invalid boolean value for shell-expand-args option: '%s'", option_arg ? option_arg : "<null>"); + break; + } + case 'c': if (option_arg && option_arg[0]) launch_info.SetShell (FileSpec(option_arg, false)); @@ -518,7 +532,7 @@ ProcessLaunchCommandOptions::g_option_table[] = { LLDB_OPT_SET_ALL, false, "working-dir", 'w', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeDirectoryName, "Set the current working directory to <path> when running the inferior."}, { LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."}, { LLDB_OPT_SET_ALL, false, "environment", 'v', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeNone, "Specify an environment variable name/value string (--environment NAME=VALUE). Can be specified multiple times for subsequent environment entries."}, -{ LLDB_OPT_SET_ALL, false, "shell", 'c', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, +{ LLDB_OPT_SET_1|LLDB_OPT_SET_2|LLDB_OPT_SET_3, false, "shell", 'c', OptionParser::eOptionalArgument, NULL, NULL, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, { LLDB_OPT_SET_1 , false, "stdin", 'i', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Redirect stdin for the process to <filename>."}, { LLDB_OPT_SET_1 , false, "stdout", 'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFilename, "Redirect stdout for the process to <filename>."}, @@ -527,7 +541,7 @@ ProcessLaunchCommandOptions::g_option_table[] = { LLDB_OPT_SET_2 , false, "tty", 't', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."}, { LLDB_OPT_SET_3 , false, "no-stdio", 'n', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, - +{ LLDB_OPT_SET_4, false, "shell-expand-args", 'X', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "Set whether to shell expand arguments to the process when launching."}, { 0 , false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -688,7 +702,7 @@ Process::Process(Target &target, Listener &listener) : Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_signals_sp) : ProcessProperties (this), UserID (LLDB_INVALID_PROCESS_ID), - Broadcaster (&(target.GetDebugger()), "lldb.process"), + Broadcaster (&(target.GetDebugger()), Process::GetStaticBroadcasterClass().AsCString()), m_target (target), m_public_state (eStateUnloaded), m_private_state (eStateUnloaded), @@ -720,12 +734,12 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_process_input_reader (), m_stdio_communication ("process.stdio"), m_stdio_communication_mutex (Mutex::eMutexTypeRecursive), - m_stdio_disable(true), + m_stdin_forward (false), m_stdout_data (), m_stderr_data (), m_profile_data_comm_mutex (Mutex::eMutexTypeRecursive), m_profile_data (), - m_iohandler_sync (false), + m_iohandler_sync (0), m_memory_cache (*this), m_allocated_memory_cache (*this), m_should_detach (false), @@ -734,9 +748,10 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_private_run_lock (), m_currently_handling_event(false), m_stop_info_override_callback (NULL), - m_finalize_called(false), + m_finalizing (false), + m_finalize_called (false), m_clear_thread_plans_on_stop (false), - m_force_next_event_delivery(false), + m_force_next_event_delivery (false), m_last_broadcast_state (eStateInvalid), m_destroy_in_process (false), m_can_jit(eCanJITDontKnow) @@ -807,6 +822,9 @@ Process::GetGlobalProperties() void Process::Finalize() { + m_finalizing = true; + + // Destroy this process if needed switch (GetPrivateState()) { case eStateConnected: @@ -817,14 +835,7 @@ Process::Finalize() case eStateStepping: case eStateCrashed: case eStateSuspended: - if (GetShouldDetach()) - { - // FIXME: This will have to be a process setting: - bool keep_stopped = false; - Detach(keep_stopped); - } - else - Destroy(); + Destroy(false); break; case eStateInvalid: @@ -862,6 +873,9 @@ Process::Finalize() m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); m_stop_info_override_callback = NULL; + // Clear the last natural stop ID since it has a strong + // reference to this process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); // EventSP event_sp; @@ -937,33 +951,21 @@ Process::GetNextEvent (EventSP &event_sp) return state; } -bool -Process::SyncIOHandler (uint64_t timeout_msec) +void +Process::SyncIOHandler (uint32_t iohandler_id, uint64_t timeout_msec) { - bool timed_out = false; - // don't sync (potentially context switch) in case where there is no process IO - if (m_process_input_reader) - { - TimeValue timeout = TimeValue::Now(); - timeout.OffsetWithMicroSeconds(timeout_msec*1000); - - m_iohandler_sync.WaitForValueEqualTo(true, &timeout, &timed_out); - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - if(log) - { - if(timed_out) - log->Printf ("Process::%s pid %" PRIu64 " (timeout=%" PRIu64 "ms): FAIL", __FUNCTION__, GetID (), timeout_msec); - else - log->Printf ("Process::%s pid %" PRIu64 ": SUCCESS", __FUNCTION__, GetID ()); - } + if (! m_process_input_reader) + return; - // reset sync one-shot so it will be ready for next launch - m_iohandler_sync.SetValue(false, eBroadcastNever); - } + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithMicroSeconds(timeout_msec*1000); + uint32_t new_iohandler_id = 0; + m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, new_iohandler_id, &timeout); - return !timed_out; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::%s waited for m_iohandler_sync to change from %u, new value is %u", __FUNCTION__, iohandler_id, new_iohandler_id); } StateType @@ -991,10 +993,15 @@ Process::WaitForProcessToStop (const TimeValue *timeout, if (!wait_always && StateIsStoppedState(state, true) && - StateIsStoppedState(GetPrivateState(), true)) { + StateIsStoppedState(GetPrivateState(), true)) + { if (log) log->Printf("Process::%s returning without waiting for events; process private and public states are already 'stopped'.", __FUNCTION__); + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener) + m_public_run_lock.SetStopped(); return state; } @@ -1422,6 +1429,9 @@ Process::GetExitDescription () bool Process::SetExitStatus (int status, const char *cstr) { + // Use a mutex to protect setting the exit status. + Mutex::Locker locker (m_exit_status_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)", @@ -1437,21 +1447,35 @@ Process::SetExitStatus (int status, const char *cstr) log->Printf("Process::SetExitStatus () ignoring exit status because state was already set to eStateExited"); return false; } - - // use a mutex to protect the status and string during updating - { - Mutex::Locker locker (m_exit_status_mutex); - m_exit_status = status; - if (cstr) - m_exit_string = cstr; - else - m_exit_string.clear(); + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + + // When we exit, we no longer need to the communication channel + m_stdio_communication.Disconnect(); + m_stdio_communication.StopReadThread(); + m_stdin_forward = false; + + // And we don't need the input reader anymore as well + if (m_process_input_reader) + { + m_process_input_reader->SetIsDone(true); + m_process_input_reader->Cancel(); + m_process_input_reader.reset(); } - DidExit (); + // Clear the last natural stop ID since it has a strong + // reference to this process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); SetPrivateState (eStateExited); + + // Allow subclasses to do some cleanup + DidExit (); + return true; } @@ -1531,10 +1555,24 @@ Process::UpdateThreadListIfNeeded () for (size_t i=0; i<num_old_threads; ++i) old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread(); + // Turn off dynamic types to ensure we don't run any expressions. Objective C + // can run an expression to determine if a SBValue is a dynamic type or not + // and we need to avoid this. OperatingSystem plug-ins can't run expressions + // that require running code... + + Target &target = GetTarget(); + const lldb::DynamicValueType saved_prefer_dynamic = target.GetPreferDynamicValue (); + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(lldb::eNoDynamicValues); + // Now let the OperatingSystem plug-in update the thread list + os->UpdateThreadList (old_thread_list, // Old list full of threads created by OS plug-in real_thread_list, // The actual thread list full of threads created by each lldb_private::Process subclass new_thread_list); // The new thread list that we will show to the user that gets filled in + + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(saved_prefer_dynamic); } else { @@ -1714,15 +1752,17 @@ Process::ResumeSynchronous (Stream *stream) HijackProcessEvents(listener_sp.get()); Error error = PrivateResume(); - - StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); + if (error.Success()) + { + StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); + const bool must_be_alive = false; // eStateExited is ok, so this must be false + if (!StateIsStoppedState(state, must_be_alive)) + error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); + } // Undo the hijacking of process events... RestoreProcessEvents(); - if (error.Success() && !StateIsStoppedState(state, false)) - error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); - return error; } @@ -1763,6 +1803,7 @@ Process::SetPrivateState (StateType new_state) if (state_changed) { m_private_state.SetValueNoLock (new_state); + EventSP event_sp (new Event (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state))); if (StateIsStoppedState(new_state, false)) { // Note, this currently assumes that all threads in the list @@ -1779,15 +1820,18 @@ Process::SetPrivateState (StateType new_state) m_thread_list.DidStop(); m_mod_id.BumpStopID(); + if (!m_mod_id.IsLastResumeForUserExpression()) + m_mod_id.SetStopEventForLastNaturalStopID(event_sp); m_memory_cache.Clear(); if (log) log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_mod_id.GetStopID()); } + // Use our target to get a shared pointer to ourselves... if (m_finalize_called && PrivateStateThreadIsValid() == false) - BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state)); + BroadcastEvent (event_sp); else - m_private_state_broadcaster.BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state)); + m_private_state_broadcaster.BroadcastEvent (event_sp); } else { @@ -1819,6 +1863,12 @@ Process::GetImageInfoAddress() uint32_t Process::LoadImage (const FileSpec &image_spec, Error &error) { + if (m_finalizing) + { + error.SetErrorString("process is tearing itself down"); + return LLDB_INVALID_IMAGE_TOKEN; + } + char path[PATH_MAX]; image_spec.GetPath(path, sizeof(path)); @@ -1938,6 +1988,13 @@ Error Process::UnloadImage (uint32_t image_token) { Error error; + + if (m_finalizing) + { + error.SetErrorString("process is tearing itself down"); + return error; + } + if (image_token < m_image_tokens.size()) { const addr_t image_addr = m_image_tokens[image_token]; @@ -2020,6 +2077,9 @@ Process::GetABI() LanguageRuntime * Process::GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null) { + if (m_finalizing) + return nullptr; + LanguageRuntimeCollection::iterator pos; pos = m_language_runtimes.find (language); if (pos == m_language_runtimes.end() || (retry_if_null && !(*pos).second)) @@ -2054,6 +2114,9 @@ Process::GetObjCLanguageRuntime (bool retry_if_null) bool Process::IsPossibleDynamicValue (ValueObject& in_value) { + if (m_finalizing) + return false; + if (in_value.IsDynamic()) return false; LanguageType known_type = in_value.GetObjectRuntimeLanguage(); @@ -2072,6 +2135,12 @@ Process::IsPossibleDynamicValue (ValueObject& in_value) return objc_runtime ? objc_runtime->CouldHaveDynamicValue(in_value) : false; } +void +Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) +{ + m_dynamic_checkers_ap.reset(dynamic_checkers); +} + BreakpointSiteList & Process::GetBreakpointSiteList() { @@ -2177,11 +2246,12 @@ Process::CreateBreakpointSite (const BreakpointLocationSP &owner, bool use_hardw if (symbol && symbol->IsIndirect()) { Error error; - load_addr = ResolveIndirectFunction (&symbol->GetAddress(), error); + Address symbol_address = symbol->GetAddress(); + load_addr = ResolveIndirectFunction (&symbol_address, error); if (!error.Success() && show_error) { m_target.GetDebugger().GetErrorFile()->Printf ("warning: failed to resolve indirect function at 0x%" PRIx64 " for breakpoint %i.%i: %s\n", - symbol->GetAddress().GetLoadAddress(&m_target), + symbol->GetLoadAddress(&m_target), owner->GetBreakpoint().GetID(), owner->GetID(), error.AsCString() ? error.AsCString() : "unknown error"); @@ -2403,7 +2473,7 @@ Process::DisableSoftwareBreakpoint (BreakpointSite *bp_site) if (DoReadMemory (bp_addr, curr_break_op, break_op_size, error) == break_op_size) { bool verify = false; - // Make sure we have the a breakpoint opcode exists at this address + // Make sure the breakpoint opcode exists at this address if (::memcmp (curr_break_op, break_op, break_op_size) == 0) { break_op_found = true; @@ -2968,6 +3038,33 @@ Process::ReadModuleFromMemory (const FileSpec& file_spec, return ModuleSP(); } +bool +Process::GetLoadAddressPermissions (lldb::addr_t load_addr, uint32_t &permissions) +{ + MemoryRegionInfo range_info; + permissions = 0; + Error error (GetMemoryRegionInfo (load_addr, range_info)); + if (!error.Success()) + return false; + if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow + || range_info.GetWritable() == MemoryRegionInfo::eDontKnow + || range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) + { + return false; + } + + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsReadable; + + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsWritable; + + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsExecutable; + + return true; +} + Error Process::EnableWatchpoint (Watchpoint *watchpoint, bool notify) { @@ -3090,8 +3187,9 @@ Process::Launch (ProcessLaunchInfo &launch_info) { // We were able to launch the process, but we failed to // catch the initial stop. + error.SetErrorString ("failed to catch stop after launch"); SetExitStatus (0, "failed to catch stop after launch"); - Destroy(); + Destroy(false); } else if (state == eStateStopped || state == eStateCrashed) { @@ -3125,7 +3223,7 @@ Process::Launch (ProcessLaunchInfo &launch_info) // Target was stopped at entry as was intended. Need to notify the listeners // about it. - if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == true) + if (state == eStateStopped && launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) HandlePrivateEvent(event_sp); } else if (state == eStateExited) @@ -3152,6 +3250,9 @@ Process::LoadCore () Error error = DoLoadCore(); if (error.Success()) { + Listener listener ("lldb.process.load_core_listener"); + HijackProcessEvents(&listener); + if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else @@ -3172,7 +3273,20 @@ Process::LoadCore () // show all of the threads in the core file and explore the crashed // state. SetPrivateState (eStateStopped); - + + // Wait indefinitely for a stopped event since we just posted one above... + lldb::EventSP event_sp; + listener.WaitForEvent (NULL, event_sp); + StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (!StateIsStoppedState (state, false)) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::Halt() failed to stop, state is: %s", StateAsCString(state)); + error.SetErrorString ("Did not get stopped event after loading the core file."); + } + RestoreProcessEvents (); } return error; } @@ -3230,6 +3344,9 @@ Process::AttachCompletionHandler::PerformAction (lldb::EventSP &event_sp) switch (state) { + case eStateAttaching: + return eEventActionSuccess; + case eStateRunning: case eStateConnected: return eEventActionRetry; @@ -3702,7 +3819,7 @@ Process::Halt (bool clear_thread_plans) RestorePrivateProcessEvents(); restored_process_events = true; SetExitStatus(SIGKILL, "Cancelled async attach."); - Destroy (); + Destroy (false); } else { @@ -3876,13 +3993,23 @@ Process::Detach (bool keep_stopped) } Error -Process::Destroy () +Process::Destroy (bool force_kill) { // Tell ourselves we are in the process of destroying the process, so that we don't do any unnecessary work // that might hinder the destruction. Remember to set this back to false when we are done. That way if the attempt // failed and the process stays around for some reason it won't be in a confused state. + + if (force_kill) + m_should_detach = false; + if (GetShouldDetach()) + { + // FIXME: This will have to be a process setting: + bool keep_stopped = false; + Detach(keep_stopped); + } + m_destroy_in_process = true; Error error (WillDestroy()); @@ -3910,9 +4037,9 @@ Process::Destroy () DidDestroy(); StopPrivateStateThread(); } - m_stdio_communication.StopReadThread(); m_stdio_communication.Disconnect(); - m_stdio_disable = true; + m_stdio_communication.StopReadThread(); + m_stdin_forward = false; if (m_process_input_reader) { @@ -3954,6 +4081,20 @@ Process::Signal (int signal) return error; } +void +Process::SetUnixSignals (const UnixSignalsSP &signals_sp) +{ + assert (signals_sp && "null signals_sp"); + m_unix_signals_sp = signals_sp; +} + +UnixSignals & +Process::GetUnixSignals () +{ + assert (m_unix_signals_sp && "null m_unix_signals_sp"); + return *m_unix_signals_sp; +} + lldb::ByteOrder Process::GetByteOrder () const { @@ -3976,12 +4117,14 @@ Process::ShouldBroadcastEvent (Event *event_ptr) switch (state) { - case eStateConnected: - case eStateAttaching: - case eStateLaunching: case eStateDetached: case eStateExited: case eStateUnloaded: + m_stdio_communication.SynchronizeWithReadThread(); + // fall-through + case eStateConnected: + case eStateAttaching: + case eStateLaunching: // These events indicate changes in the state of the debugging session, always report them. return_value = true; break; @@ -4041,6 +4184,7 @@ Process::ShouldBroadcastEvent (Event *event_ptr) // If we aren't going to stop, let the thread plans decide if we're going to report this event. // If no thread has an opinion, we don't report it. + m_stdio_communication.SynchronizeWithReadThread(); RefreshStateAfterStop (); if (ProcessEventData::GetInterruptedFromEvent (event_ptr)) { @@ -4128,7 +4272,7 @@ Process::ShouldBroadcastEvent (Event *event_ptr) bool -Process::StartPrivateStateThread (bool force) +Process::StartPrivateStateThread (bool is_secondary_thread) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); @@ -4136,7 +4280,7 @@ Process::StartPrivateStateThread (bool force) if (log) log->Printf ("Process::%s()%s ", __FUNCTION__, already_running ? " already running" : " starting private state thread"); - if (!force && already_running) + if (!is_secondary_thread && already_running) return true; // Create a thread that watches our internal state and controls which @@ -4160,7 +4304,8 @@ Process::StartPrivateStateThread (bool force) } // Create the private state thread, and start it running. - m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, this, NULL); + PrivateStateThreadArgs args = {this, is_secondary_thread}; + m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, (void *) &args, NULL); if (m_private_state_thread.IsJoinable()) { ResumePrivateStateThread(); @@ -4324,13 +4469,17 @@ Process::HandlePrivateEvent (EventSP &event_sp) // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... // Or don't push it if we are launching since it will come up stopped. - if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching) + if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching && + new_state != eStateAttaching) + { PushProcessIOHandler (); - m_iohandler_sync.SetValue(true, eBroadcastAlways); + m_iohandler_sync.SetValue(m_iohandler_sync.GetValue()+1, eBroadcastAlways); + if (log) + log->Printf("Process::%s updated m_iohandler_sync to %d", __FUNCTION__, m_iohandler_sync.GetValue()); + } } else if (StateIsStoppedState(new_state, false)) { - m_iohandler_sync.SetValue(false, eBroadcastNever); if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { // If the lldb_private::Debugger is handling the events, we don't @@ -4386,13 +4535,13 @@ Process::HandlePrivateEvent (EventSP &event_sp) thread_result_t Process::PrivateStateThread (void *arg) { - Process *proc = static_cast<Process*> (arg); - thread_result_t result = proc->RunPrivateStateThread(); + PrivateStateThreadArgs *real_args = static_cast<PrivateStateThreadArgs *> (arg); + thread_result_t result = real_args->process->RunPrivateStateThread(real_args->is_secondary_thread); return result; } thread_result_t -Process::RunPrivateStateThread () +Process::RunPrivateStateThread (bool is_secondary_thread) { bool control_only = true; m_private_state_control_wait.SetValue (false, eBroadcastNever); @@ -4484,7 +4633,11 @@ Process::RunPrivateStateThread () log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, static_cast<void*>(this), GetID()); - m_public_run_lock.SetStopped(); + // If we are a secondary thread, then the primary thread we are working for will have already + // acquired the public_run_lock, and isn't done with what it was doing yet, so don't + // try to change it on the way out. + if (!is_secondary_thread) + m_public_run_lock.SetStopped(); m_private_state_control_wait.SetValue (true, eBroadcastAlways); m_private_state_thread.Reset(); return NULL; @@ -4496,7 +4649,7 @@ Process::RunPrivateStateThread () Process::ProcessEventData::ProcessEventData () : EventData (), - m_process_sp (), + m_process_wp (), m_state (eStateInvalid), m_restarted (false), m_update_state (0), @@ -4506,12 +4659,14 @@ Process::ProcessEventData::ProcessEventData () : Process::ProcessEventData::ProcessEventData (const ProcessSP &process_sp, StateType state) : EventData (), - m_process_sp (process_sp), + m_process_wp (), m_state (state), m_restarted (false), m_update_state (0), m_interrupted (false) { + if (process_sp) + m_process_wp = process_sp; } Process::ProcessEventData::~ProcessEventData() @@ -4534,6 +4689,11 @@ Process::ProcessEventData::GetFlavor () const void Process::ProcessEventData::DoOnRemoval (Event *event_ptr) { + ProcessSP process_sp(m_process_wp.lock()); + + if (!process_sp) + return; + // This function gets called twice for each event, once when the event gets pulled // off of the private process event queue, and then any number of times, first when it gets pulled off of // the public event queue, then other times when we're pretending that this is where we stopped at the @@ -4543,7 +4703,7 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr) if (m_update_state != 1) return; - m_process_sp->SetPublicState (m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); + process_sp->SetPublicState (m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); // If this is a halt event, even if the halt stopped with some reason other than a plain interrupt (e.g. we had // already stopped for a breakpoint when the halt request came through) don't do the StopInfo actions, as they may @@ -4554,7 +4714,7 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr) // If we're stopped and haven't restarted, then do the StopInfo actions here: if (m_state == eStateStopped && ! m_restarted) { - ThreadList &curr_thread_list = m_process_sp->GetThreadList(); + ThreadList &curr_thread_list = process_sp->GetThreadList(); uint32_t num_threads = curr_thread_list.GetSize(); uint32_t idx; @@ -4584,7 +4744,7 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr) for (idx = 0; idx < num_threads; ++idx) { - curr_thread_list = m_process_sp->GetThreadList(); + curr_thread_list = process_sp->GetThreadList(); if (curr_thread_list.GetSize() != num_threads) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); @@ -4647,14 +4807,14 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr) SetRestarted(true); // Use the public resume method here, since this is just // extending a public resume. - m_process_sp->PrivateResume(); + process_sp->PrivateResume(); } else { // If we didn't restart, run the Stop Hooks here: // They might also restart the target, so watch for that. - m_process_sp->GetTarget().RunStopHooks(); - if (m_process_sp->GetPrivateState() == eStateRunning) + process_sp->GetTarget().RunStopHooks(); + if (process_sp->GetPrivateState() == eStateRunning) SetRestarted(true); } } @@ -4664,9 +4824,13 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr) void Process::ProcessEventData::Dump (Stream *s) const { - if (m_process_sp) + ProcessSP process_sp(m_process_wp.lock()); + + if (process_sp) s->Printf(" process = %p (pid = %" PRIu64 "), ", - static_cast<void*>(m_process_sp.get()), m_process_sp->GetID()); + static_cast<void*>(process_sp.get()), process_sp->GetID()); + else + s->PutCString(" process = NULL, "); s->Printf("state = %s", StateAsCString(GetState())); } @@ -4946,6 +5110,7 @@ public: m_write_file (write_fd, false), m_pipe () { + m_pipe.CreateNew(false); m_read_file.SetDescriptor(GetInputFD(), false); } @@ -4955,130 +5120,93 @@ public: } - bool - OpenPipes () - { - if (m_pipe.CanRead() && m_pipe.CanWrite()) - return true; - Error result = m_pipe.CreateNew(false); - return result.Success(); - } - - void - ClosePipes() - { - m_pipe.Close(); - } - // Each IOHandler gets to run until it is done. It should read data // from the "in" and place output into "out" and "err and return // when done. - virtual void - Run () + void + Run () override { - if (m_read_file.IsValid() && m_write_file.IsValid()) + if (!m_read_file.IsValid() || !m_write_file.IsValid() || !m_pipe.CanRead() || !m_pipe.CanWrite()) { - SetIsDone(false); - if (OpenPipes()) - { - const int read_fd = m_read_file.GetDescriptor(); - const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); - TerminalState terminal_state; - terminal_state.Save (read_fd, false); - Terminal terminal(read_fd); - terminal.SetCanonical(false); - terminal.SetEcho(false); + SetIsDone(true); + return; + } + + SetIsDone(false); + const int read_fd = m_read_file.GetDescriptor(); + TerminalState terminal_state; + terminal_state.Save (read_fd, false); + Terminal terminal(read_fd); + terminal.SetCanonical(false); + terminal.SetEcho(false); // FD_ZERO, FD_SET are not supported on windows #ifndef _WIN32 - while (!GetIsDone()) + const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); + while (!GetIsDone()) + { + fd_set read_fdset; + FD_ZERO (&read_fdset); + FD_SET (read_fd, &read_fdset); + FD_SET (pipe_read_fd, &read_fdset); + const int nfds = std::max<int>(read_fd, pipe_read_fd) + 1; + int num_set_fds = select (nfds, &read_fdset, NULL, NULL, NULL); + if (num_set_fds < 0) + { + const int select_errno = errno; + + if (select_errno != EINTR) + SetIsDone(true); + } + else if (num_set_fds > 0) + { + char ch = 0; + size_t n; + if (FD_ISSET (read_fd, &read_fdset)) { - fd_set read_fdset; - FD_ZERO (&read_fdset); - FD_SET (read_fd, &read_fdset); - FD_SET (pipe_read_fd, &read_fdset); - const int nfds = std::max<int>(read_fd, pipe_read_fd) + 1; - int num_set_fds = select (nfds, &read_fdset, NULL, NULL, NULL); - if (num_set_fds < 0) + n = 1; + if (m_read_file.Read(&ch, n).Success() && n == 1) { - const int select_errno = errno; - - if (select_errno != EINTR) + if (m_write_file.Write(&ch, n).Fail() || n != 1) SetIsDone(true); } - else if (num_set_fds > 0) + else + SetIsDone(true); + } + if (FD_ISSET (pipe_read_fd, &read_fdset)) + { + size_t bytes_read; + // Consume the interrupt byte + Error error = m_pipe.Read(&ch, 1, bytes_read); + if (error.Success()) { - char ch = 0; - size_t n; - if (FD_ISSET (read_fd, &read_fdset)) + switch (ch) { - n = 1; - if (m_read_file.Read(&ch, n).Success() && n == 1) - { - if (m_write_file.Write(&ch, n).Fail() || n != 1) - SetIsDone(true); - } - else + case 'q': SetIsDone(true); - } - if (FD_ISSET (pipe_read_fd, &read_fdset)) - { - size_t bytes_read; - // Consume the interrupt byte - Error error = m_pipe.Read(&ch, 1, bytes_read); - if (error.Success()) - { - switch (ch) - { - case 'q': - SetIsDone(true); - break; - case 'i': - if (StateIsRunningState(m_process->GetState())) - m_process->Halt(); - break; - } - } + break; + case 'i': + if (StateIsRunningState(m_process->GetState())) + m_process->Halt(); + break; } } } -#endif - terminal_state.Restore(); - } - else - SetIsDone(true); } - else - SetIsDone(true); - } - - // Hide any characters that have been displayed so far so async - // output can be displayed. Refresh() will be called after the - // output has been displayed. - virtual void - Hide () - { - - } - // Called when the async output has been received in order to update - // the input reader (refresh the prompt and redisplay any current - // line(s) that are being edited - virtual void - Refresh () - { - +#endif + terminal_state.Restore(); } - virtual void - Cancel () + void + Cancel () override { char ch = 'q'; // Send 'q' for quit size_t bytes_written = 0; m_pipe.Write(&ch, 1, bytes_written); } - virtual bool - Interrupt () + bool + Interrupt () override { // Do only things that are safe to do in an interrupt context (like in // a SIGINT handler), like write 1 byte to a file descriptor. This will @@ -5111,8 +5239,8 @@ public: return false; } - virtual void - GotEOF() + void + GotEOF() override { } @@ -5161,6 +5289,10 @@ Process::PushProcessIOHandler () IOHandlerSP io_handler_sp (m_process_input_reader); if (io_handler_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::%s pushing IO handler", __FUNCTION__); + io_handler_sp->SetIsDone(false); m_target.GetDebugger().PushIOHandler (io_handler_sp); return true; @@ -6252,6 +6384,15 @@ Process::ClearPreResumeActions () m_pre_resume_actions.clear(); } +ProcessRunLock & +Process::GetRunLock() +{ + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) + return m_private_run_lock; + else + return m_public_run_lock; +} + void Process::Flush () { @@ -6351,13 +6492,29 @@ Process::ModulesDidLoad (ModuleList &module_list) runtime->ModulesDidLoad(module_list); } + // Let any language runtimes we have already created know + // about the modules that loaded. + + // Iterate over a copy of this language runtime list in case + // the language runtime ModulesDidLoad somehow causes the language + // riuntime to be unloaded. + LanguageRuntimeCollection language_runtimes(m_language_runtimes); + for (const auto &pair: language_runtimes) + { + // We must check language_runtime_sp to make sure it is not + // NULL as we might cache the fact that we didn't have a + // language runtime for a language. + LanguageRuntimeSP language_runtime_sp = pair.second; + if (language_runtime_sp) + language_runtime_sp->ModulesDidLoad(module_list); + } } ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { ThreadCollectionSP threads; - + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(shared_from_this()); if (! memory_history.get()) { @@ -6381,3 +6538,12 @@ Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) else return (*pos).second; } + +bool +Process::GetModuleSpec(const FileSpec& module_file_spec, + const ArchSpec& arch, + ModuleSpec& module_spec) +{ + module_spec.Clear(); + return false; +} |