Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -176,16 +176,6 @@ ArchSpec m_arch; std::unique_ptr m_monitor_up; - HostThread m_operation_thread; - - // current operation which must be executed on the priviliged thread - void *m_operation; - Mutex m_operation_mutex; - - // semaphores notified when Operation is ready to be processed and when - // the operation is complete. - sem_t m_operation_pending; - sem_t m_operation_done; LazyBool m_supports_mem_region; std::vector m_mem_region_cache; @@ -198,25 +188,13 @@ // the relevan breakpoint std::map m_threads_stepping_with_breakpoint; - struct OperationArgs - { - OperationArgs(NativeProcessLinux *monitor); - - ~OperationArgs(); - - NativeProcessLinux *m_monitor; // The monitor performing the attach. - sem_t m_semaphore; // Posted to once operation complete. - Error m_error; // Set if process operation failed. - }; - /// @class LauchArgs /// /// @brief Simple structure to pass data to the thread responsible for /// launching a child process. - struct LaunchArgs : OperationArgs + struct LaunchArgs { - LaunchArgs(NativeProcessLinux *monitor, - Module *module, + LaunchArgs(Module *module, char const **argv, char const **envp, const std::string &stdin_path, @@ -237,15 +215,7 @@ const ProcessLaunchInfo &m_launch_info; }; - struct AttachArgs : OperationArgs - { - AttachArgs(NativeProcessLinux *monitor, - lldb::pid_t pid); - - ~AttachArgs(); - - lldb::pid_t m_pid; // pid of the process to be attached. - }; + typedef std::function<::pid_t(Error &)> InitialOperation; // --------------------------------------------------------------------- // Private Instance Methods @@ -272,32 +242,17 @@ AttachToInferior (lldb::pid_t pid, Error &error); void - StartMonitorThread(Error &error); - - void - StartLaunchOpThread(LaunchArgs *args, Error &error); + StartMonitorThread(const InitialOperation &operation, Error &error); - static void * - LaunchOpThread(void *arg); - - static bool - Launch(LaunchArgs *args); + ::pid_t + Launch(LaunchArgs *args, Error &error); - void - StartAttachOpThread(AttachArgs *args, Error &error); - - static void * - AttachOpThread(void *args); - - static bool - Attach(AttachArgs *args); + ::pid_t + Attach(lldb::pid_t pid, Error &error); static Error SetDefaultPtraceOpts(const lldb::pid_t); - static void - ServeOperation(OperationArgs *args); - static bool DupDescriptor(const char *path, int fd, int flags); @@ -336,13 +291,6 @@ GetCrashReasonForSIGBUS(const siginfo_t *info); #endif - void - DoOperation(void *op); - - /// Stops the operation thread used to attach/launch a process. - void - StopOpThread(); - Error StartCoordinatorThread (); Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -146,8 +146,6 @@ // Private bits we only need internally. namespace { - static void * const EXIT_OPERATION = nullptr; - const UnixSignals& GetUnixSignals () { @@ -1074,16 +1072,59 @@ } // end of anonymous namespace +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static bool +EnsureFDFlags(int fd, int flags, Error &error) +{ + int status; + + if ((status = fcntl(fd, F_GETFL)) == -1) + { + error.SetErrorToErrno(); + return false; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) + { + error.SetErrorToErrno(); + return false; + } + + return true; +} + +// This class encapsulates the privileged thread which performs all ptrace and wait operations on +// the inferior. The thread consists of a main loop which waits for events and processes them +// - SIGCHLD (delivered over a signalfd file descriptor): These signals notify us of events in +// the inferior process. Upon receiving this signal we do a waitpid to get more information +// and dispatch to NativeProcessLinux::MonitorCallback. +// - requests for ptrace operations: These initiated via the DoOperation method, which funnels +// them to the Monitor thread via m_operation member. The Monitor thread is signaled over a +// pipe, and the completion of the operation is signalled over the semaphore. +// - thread exit event: this is signaled from the Monitor destructor by closing the write end +// of the command pipe. class NativeProcessLinux::Monitor { private: - ::pid_t m_child_pid; - NativeProcessLinux *m_native_process; + // The initial monitor operation (launch or attach). It returns a inferior process id. + std::unique_ptr m_initial_operation_up; + + ::pid_t m_child_pid = -1; + NativeProcessLinux * m_native_process; enum { READ, WRITE }; int m_pipefd[2] = {-1, -1}; int m_signal_fd = -1; HostThread m_thread; + // current operation which must be executed on the priviliged thread + Operation *m_operation = nullptr; + Mutex m_operation_mutex; + sem_t m_operation_sem; + Error m_operation_error; + + static constexpr char operation_command = 'o'; + void HandleSignals(); @@ -1100,17 +1141,26 @@ static void * RunMonitor(void *arg); + Error + WaitForOperation(); public: - // Takes ownership of command_fd. - Monitor(::pid_t child_pid, NativeProcessLinux *native_process) - : m_child_pid(-getpgid(child_pid)), m_native_process(native_process) - {} + Monitor(const InitialOperation &initial_operation, + NativeProcessLinux *native_process) + : m_initial_operation_up(new InitialOperation(initial_operation)), + m_native_process(native_process) + { + sem_init(&m_operation_sem, 0, 0); + } ~Monitor(); Error Initialize(); + + void + DoOperation(Operation *op); }; +constexpr char NativeProcessLinux::Monitor::operation_command; Error NativeProcessLinux::Monitor::Initialize() @@ -1137,11 +1187,36 @@ return error; } - m_thread = ThreadLauncher::LaunchThread("monitor", Monitor::RunMonitor, this, nullptr); + if (! EnsureFDFlags(m_pipefd[READ], O_NONBLOCK, error)) { + return error; + } + + static const char g_thread_name[] = "lldb.process.nativelinux.monitor"; + m_thread = ThreadLauncher::LaunchThread(g_thread_name, Monitor::RunMonitor, this, nullptr); if (!m_thread.IsJoinable()) return Error("Failed to create monitor thread for NativeProcessLinux."); - return error; + return WaitForOperation(); +} + +void +NativeProcessLinux::Monitor::DoOperation(Operation *op) +{ + if (m_thread.EqualsThread(pthread_self())) { + // If we're on the Monitor thread, we can simply execute the operation. + op->Execute(m_native_process); + return; + } + + // Otherwise we need to pass the operation to the Monitor thread so it can handle it. + Mutex::Locker lock(m_operation_mutex); + + m_operation = op; + + // notify the thread that an operation is ready to be processed + write(m_pipefd[WRITE], &operation_command, 1); + + WaitForOperation(); } NativeProcessLinux::Monitor::~Monitor() @@ -1154,6 +1229,7 @@ close(m_pipefd[READ]); if (m_signal_fd >= 0) close(m_signal_fd); + sem_destroy(&m_operation_sem); } void @@ -1280,15 +1356,31 @@ log->Printf("NativeProcessLinux::Monitor::%s exit command received, exiting...", __FUNCTION__); return true; // We are done. } - if (log) - log->Printf("NativeProcessLinux::Monitor::%s received unknown command '%c'", - __FUNCTION__, command); + + switch (command) + { + case operation_command: + m_operation->Execute(m_native_process); + + // notify calling thread that operation is complete + sem_post(&m_operation_sem); + break; + default: + if (log) + log->Printf("NativeProcessLinux::Monitor::%s received unknown command '%c'", + __FUNCTION__, command); + } } } void NativeProcessLinux::Monitor::MainLoop() { + ::pid_t child_pid = (*m_initial_operation_up)(m_operation_error); + m_initial_operation_up.reset(); + m_child_pid = -getpgid(child_pid), + sem_post(&m_operation_sem); + while (true) { fd_set fds; @@ -1321,49 +1413,31 @@ } } -void * -NativeProcessLinux::Monitor::RunMonitor(void *arg) -{ - static_cast(arg)->MainLoop(); - return nullptr; -} - - -// Simple helper function to ensure flags are enabled on the given file -// descriptor. -static bool -EnsureFDFlags(int fd, int flags, Error &error) +Error +NativeProcessLinux::Monitor::WaitForOperation() { - int status; - - if ((status = fcntl(fd, F_GETFL)) == -1) + Error error; + while (sem_wait(&m_operation_sem) != 0) { - error.SetErrorToErrno(); - return false; - } + if (errno == EINTR) + continue; - if (fcntl(fd, F_SETFL, status | flags) == -1) - { error.SetErrorToErrno(); - return false; + return error; } - return true; + return m_operation_error; } -NativeProcessLinux::OperationArgs::OperationArgs(NativeProcessLinux *monitor) - : m_monitor(monitor) +void * +NativeProcessLinux::Monitor::RunMonitor(void *arg) { - sem_init(&m_semaphore, 0, 0); + static_cast(arg)->MainLoop(); + return nullptr; } -NativeProcessLinux::OperationArgs::~OperationArgs() -{ - sem_destroy(&m_semaphore); -} -NativeProcessLinux::LaunchArgs::LaunchArgs(NativeProcessLinux *monitor, - Module *module, +NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module, char const **argv, char const **envp, const std::string &stdin_path, @@ -1371,8 +1445,7 @@ const std::string &stderr_path, const char *working_dir, const ProcessLaunchInfo &launch_info) - : OperationArgs(monitor), - m_module(module), + : m_module(module), m_argv(argv), m_envp(envp), m_stdin_path(stdin_path), @@ -1386,13 +1459,6 @@ NativeProcessLinux::LaunchArgs::~LaunchArgs() { } -NativeProcessLinux::AttachArgs::AttachArgs(NativeProcessLinux *monitor, - lldb::pid_t pid) - : OperationArgs(monitor), m_pid(pid) { } - -NativeProcessLinux::AttachArgs::~AttachArgs() -{ } - // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- @@ -1546,11 +1612,6 @@ NativeProcessLinux::NativeProcessLinux () : NativeProcessProtocol (LLDB_INVALID_PROCESS_ID), m_arch (), - m_operation_thread (), - m_operation (nullptr), - m_operation_mutex (), - m_operation_pending (), - m_operation_done (), m_supports_mem_region (eLazyBoolCalculate), m_mem_region_cache (), m_mem_region_cache_mutex (), @@ -1560,17 +1621,9 @@ } //------------------------------------------------------------------------------ -/// The basic design of the NativeProcessLinux is built around two threads. -/// -/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking -/// for changes in the debugee state. When a change is detected a -/// ProcessMessage is sent to the associated ProcessLinux instance. This thread -/// "drives" state changes in the debugger. -/// -/// The second thread (@see OperationThread) is responsible for two things 1) -/// launching or attaching to the inferior process, and then 2) servicing -/// operations such as register reads/writes, stepping, etc. See the comments -/// on the Operation class for more info as to why this is needed. +// NativeProcessLinux spawns a new thread which performs all operations on the inferior process. +// Refer to Monitor and Operation classes to see why this is necessary. +//------------------------------------------------------------------------------ void NativeProcessLinux::LaunchInferior ( Module *module, @@ -1590,45 +1643,17 @@ std::unique_ptr args( new LaunchArgs( - this, module, argv, envp, + module, argv, envp, stdin_path, stdout_path, stderr_path, working_dir, launch_info)); - sem_init (&m_operation_pending, 0, 0); - sem_init (&m_operation_done, 0, 0); - - StartLaunchOpThread (args.get(), error); + StartMonitorThread ([&] (Error &e) { return Launch(args.get(), e); }, error); if (!error.Success ()) return; error = StartCoordinatorThread (); if (!error.Success ()) return; - -WAIT_AGAIN: - // Wait for the operation thread to initialize. - if (sem_wait(&args->m_semaphore)) - { - if (errno == EINTR) - goto WAIT_AGAIN; - else - { - error.SetErrorToErrno(); - return; - } - } - - // Check that the launch was a success. - if (!args->m_error.Success()) - { - StopOpThread(); - StopCoordinatorThread (); - error = args->m_error; - return; - } - - // Finally, start monitoring the child process for change in state. - StartMonitorThread(error); } void @@ -1675,43 +1700,13 @@ m_pid = pid; SetState(eStateAttaching); - sem_init (&m_operation_pending, 0, 0); - sem_init (&m_operation_done, 0, 0); - - std::unique_ptr args (new AttachArgs (this, pid)); - - StartAttachOpThread(args.get (), error); + StartMonitorThread ([=] (Error &e) { return Attach(pid, e); }, error); if (!error.Success ()) return; error = StartCoordinatorThread (); if (!error.Success ()) return; - -WAIT_AGAIN: - // Wait for the operation thread to initialize. - if (sem_wait (&args->m_semaphore)) - { - if (errno == EINTR) - goto WAIT_AGAIN; - else - { - error.SetErrorToErrno (); - return; - } - } - - // Check that the attach was a success. - if (!args->m_error.Success ()) - { - StopOpThread (); - StopCoordinatorThread (); - error = args->m_error; - return; - } - - // Finally, start monitoring the child process for change in state. - StartMonitorThread(error); } void @@ -1720,43 +1715,10 @@ StopMonitor(); } -//------------------------------------------------------------------------------ -// Thread setup and tear down. - -void -NativeProcessLinux::StartLaunchOpThread(LaunchArgs *args, Error &error) -{ - static const char *g_thread_name = "lldb.process.nativelinux.operation"; - - if (m_operation_thread.IsJoinable()) - return; - - m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args, &error); -} - -void * -NativeProcessLinux::LaunchOpThread(void *arg) -{ - LaunchArgs *args = static_cast(arg); - - if (!Launch(args)) { - sem_post(&args->m_semaphore); - return NULL; - } - - ServeOperation(args); - return NULL; -} - -bool -NativeProcessLinux::Launch(LaunchArgs *args) +::pid_t +NativeProcessLinux::Launch(LaunchArgs *args, Error &error) { assert (args && "null args"); - if (!args) - return false; - - NativeProcessLinux *monitor = args->m_monitor; - assert (monitor && "monitor is NULL"); const char **argv = args->m_argv; const char **envp = args->m_envp; @@ -1776,9 +1738,9 @@ if ((pid = terminal.Fork(err_str, err_len)) == static_cast (-1)) { - args->m_error.SetErrorToGenericError(); - args->m_error.SetErrorString("Process fork failed."); - return false; + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Process fork failed: %s", err_str); + return -1; } // Recognized child exit status codes. @@ -1799,8 +1761,8 @@ // send log info to parent re: launch status, in place of the log lines removed here. // Start tracing this child that is about to exec. - PTRACE(PTRACE_TRACEME, 0, nullptr, nullptr, 0, args->m_error); - if (args->m_error.Fail()) + PTRACE(PTRACE_TRACEME, 0, nullptr, nullptr, 0, error); + if (error.Fail()) exit(ePtraceFailed); // terminal has already dupped the tty descriptors to stdin/out/err. @@ -1878,46 +1840,46 @@ int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { - args->m_error.SetErrorToErrno(); - + error.SetErrorToErrno(); if (log) - log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s", __FUNCTION__, args->m_error.AsCString ()); + log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s", + __FUNCTION__, error.AsCString ()); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. - monitor->SetState (StateType::eStateInvalid); + SetState (StateType::eStateInvalid); - return false; + return -1; } else if (WIFEXITED(status)) { // open, dup or execve likely failed for some reason. - args->m_error.SetErrorToGenericError(); + error.SetErrorToGenericError(); switch (WEXITSTATUS(status)) { case ePtraceFailed: - args->m_error.SetErrorString("Child ptrace failed."); + error.SetErrorString("Child ptrace failed."); break; case eDupStdinFailed: - args->m_error.SetErrorString("Child open stdin failed."); + error.SetErrorString("Child open stdin failed."); break; case eDupStdoutFailed: - args->m_error.SetErrorString("Child open stdout failed."); + error.SetErrorString("Child open stdout failed."); break; case eDupStderrFailed: - args->m_error.SetErrorString("Child open stderr failed."); + error.SetErrorString("Child open stderr failed."); break; case eChdirFailed: - args->m_error.SetErrorString("Child failed to set working directory."); + error.SetErrorString("Child failed to set working directory."); break; case eExecFailed: - args->m_error.SetErrorString("Child exec failed."); + error.SetErrorString("Child exec failed."); break; case eSetGidFailed: - args->m_error.SetErrorString("Child setgid failed."); + error.SetErrorString("Child setgid failed."); break; default: - args->m_error.SetErrorString("Child returned unknown exit status."); + error.SetErrorString("Child returned unknown exit status."); break; } @@ -1930,9 +1892,9 @@ // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. - monitor->SetState (StateType::eStateInvalid); + SetState (StateType::eStateInvalid); - return false; + return -1; } assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) && "Could not sync with inferior process."); @@ -1940,102 +1902,72 @@ if (log) log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__); - args->m_error = SetDefaultPtraceOpts(pid); - if (args->m_error.Fail()) + error = SetDefaultPtraceOpts(pid); + if (error.Fail()) { if (log) log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s", - __FUNCTION__, - args->m_error.AsCString ()); + __FUNCTION__, error.AsCString ()); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. - monitor->SetState (StateType::eStateInvalid); + SetState (StateType::eStateInvalid); - return false; + return -1; } // Release the master terminal descriptor and pass it off to the // NativeProcessLinux instance. Similarly stash the inferior pid. - monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); - monitor->m_pid = pid; + m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); + m_pid = pid; // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessLinux::GetSTDOUT to have a non-blocking // descriptor to read from). - if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) + if (!EnsureFDFlags(m_terminal_fd, O_NONBLOCK, error)) { if (log) log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s", - __FUNCTION__, - args->m_error.AsCString ()); + __FUNCTION__, error.AsCString ()); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. - monitor->SetState (StateType::eStateInvalid); + SetState (StateType::eStateInvalid); - return false; + return -1; } if (log) log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid); - thread_sp = monitor->AddThread (pid); + thread_sp = AddThread (pid); assert (thread_sp && "AddThread() returned a nullptr thread"); - monitor->NotifyThreadCreateStopped (pid); + NotifyThreadCreateStopped (pid); std::static_pointer_cast (thread_sp)->SetStoppedBySignal (SIGSTOP); // Let our process instance know the thread has stopped. - monitor->SetCurrentThreadID (thread_sp->GetID ()); - monitor->SetState (StateType::eStateStopped); + SetCurrentThreadID (thread_sp->GetID ()); + SetState (StateType::eStateStopped); if (log) { - if (args->m_error.Success ()) + if (error.Success ()) { log->Printf ("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__); } else { log->Printf ("NativeProcessLinux::%s inferior launching failed: %s", - __FUNCTION__, - args->m_error.AsCString ()); + __FUNCTION__, error.AsCString ()); + return -1; } } - return args->m_error.Success(); -} - -void -NativeProcessLinux::StartAttachOpThread(AttachArgs *args, Error &error) -{ - static const char *g_thread_name = "lldb.process.linux.operation"; - - if (m_operation_thread.IsJoinable()) - return; - - m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args, &error); -} - -void * -NativeProcessLinux::AttachOpThread(void *arg) -{ - AttachArgs *args = static_cast(arg); - - if (!Attach(args)) { - sem_post(&args->m_semaphore); - return nullptr; - } - - ServeOperation(args); - return nullptr; + return pid; } -bool -NativeProcessLinux::Attach(AttachArgs *args) +::pid_t +NativeProcessLinux::Attach(lldb::pid_t pid, Error &error) { - lldb::pid_t pid = args->m_pid; - - NativeProcessLinux *monitor = args->m_monitor; lldb::ThreadSP inferior; Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); @@ -2043,9 +1975,9 @@ Host::TidMap tids_to_attach; if (pid <= 1) { - args->m_error.SetErrorToGenericError(); - args->m_error.SetErrorString("Attaching to process 1 is not allowed."); - goto FINISH; + error.SetErrorToGenericError(); + error.SetErrorString("Attaching to process 1 is not allowed."); + return -1; } while (Host::FindProcessThreads(pid, tids_to_attach)) @@ -2059,18 +1991,18 @@ // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. - PTRACE(PTRACE_ATTACH, tid, nullptr, nullptr, 0, args->m_error); - if (args->m_error.Fail()) + PTRACE(PTRACE_ATTACH, tid, nullptr, nullptr, 0, error); + if (error.Fail()) { // No such thread. The thread may have exited. // More error handling may be needed. - if (args->m_error.GetError() == ESRCH) + if (error.GetError() == ESRCH) { it = tids_to_attach.erase(it); continue; } else - goto FINISH; + return -1; } int status; @@ -2087,15 +2019,14 @@ } else { - args->m_error.SetErrorToErrno(); - goto FINISH; + error.SetErrorToErrno(); + return -1; } } - args->m_error = SetDefaultPtraceOpts(tid); - if (args->m_error.Fail()) - goto FINISH; - + error = SetDefaultPtraceOpts(tid); + if (error.Fail()) + return -1; if (log) log->Printf ("NativeProcessLinux::%s() adding tid = %" PRIu64, __FUNCTION__, tid); @@ -2103,13 +2034,13 @@ it->second = true; // Create the thread, mark it as stopped. - NativeThreadProtocolSP thread_sp (monitor->AddThread (static_cast (tid))); + NativeThreadProtocolSP thread_sp (AddThread (static_cast (tid))); assert (thread_sp && "AddThread() returned a nullptr"); // This will notify this is a new thread and tell the system it is stopped. - monitor->NotifyThreadCreateStopped (tid); + NotifyThreadCreateStopped (tid); std::static_pointer_cast (thread_sp)->SetStoppedBySignal (SIGSTOP); - monitor->SetCurrentThreadID (thread_sp->GetID ()); + SetCurrentThreadID (thread_sp->GetID ()); } // move the loop forward @@ -2119,18 +2050,18 @@ if (tids_to_attach.size() > 0) { - monitor->m_pid = pid; + m_pid = pid; // Let our process instance know the thread has stopped. - monitor->SetState (StateType::eStateStopped); + SetState (StateType::eStateStopped); } else { - args->m_error.SetErrorToGenericError(); - args->m_error.SetErrorString("No such process."); + error.SetErrorToGenericError(); + error.SetErrorString("No such process."); + return -1; } - FINISH: - return args->m_error.Success(); + return pid; } Error @@ -3669,67 +3600,11 @@ } #endif -void -NativeProcessLinux::ServeOperation(OperationArgs *args) -{ - NativeProcessLinux *monitor = args->m_monitor; - - // We are finised with the arguments and are ready to go. Sync with the - // parent thread and start serving operations on the inferior. - sem_post(&args->m_semaphore); - - for(;;) - { - // wait for next pending operation - if (sem_wait(&monitor->m_operation_pending)) - { - if (errno == EINTR) - continue; - assert(false && "Unexpected errno from sem_wait"); - } - - // EXIT_OPERATION used to stop the operation thread because Cancel() isn't supported on - // android. We don't have to send a post to the m_operation_done semaphore because in this - // case the synchronization is achieved by a Join() call - if (monitor->m_operation == EXIT_OPERATION) - break; - - static_cast(monitor->m_operation)->Execute(monitor); - - // notify calling thread that operation is complete - sem_post(&monitor->m_operation_done); - } -} - -void -NativeProcessLinux::DoOperation(void *op) -{ - Mutex::Locker lock(m_operation_mutex); - - m_operation = op; - - // notify operation thread that an operation is ready to be processed - sem_post(&m_operation_pending); - - // Don't wait for the operation to complete in case of an exit operation. The operation thread - // will exit without posting to the semaphore - if (m_operation == EXIT_OPERATION) - return; - - // wait for operation to complete - while (sem_wait(&m_operation_done)) - { - if (errno == EINTR) - continue; - assert(false && "Unexpected errno from sem_wait"); - } -} - Error NativeProcessLinux::ReadMemory (lldb::addr_t addr, void *buf, lldb::addr_t size, lldb::addr_t &bytes_read) { ReadOperation op(addr, buf, size, bytes_read); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError (); } @@ -3737,7 +3612,7 @@ NativeProcessLinux::WriteMemory (lldb::addr_t addr, const void *buf, lldb::addr_t size, lldb::addr_t &bytes_written) { WriteOperation op(addr, buf, size, bytes_written); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError (); } @@ -3746,7 +3621,7 @@ uint32_t size, RegisterValue &value) { ReadRegOperation op(tid, offset, reg_name, value); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3755,7 +3630,7 @@ const char* reg_name, const RegisterValue &value) { WriteRegOperation op(tid, offset, reg_name, value); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3763,7 +3638,7 @@ NativeProcessLinux::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { ReadGPROperation op(tid, buf, buf_size); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3771,7 +3646,7 @@ NativeProcessLinux::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { ReadFPROperation op(tid, buf, buf_size); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3779,7 +3654,7 @@ NativeProcessLinux::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { ReadRegisterSetOperation op(tid, buf, buf_size, regset); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3787,7 +3662,7 @@ NativeProcessLinux::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { WriteGPROperation op(tid, buf, buf_size); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3795,7 +3670,7 @@ NativeProcessLinux::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { WriteFPROperation op(tid, buf, buf_size); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3803,7 +3678,7 @@ NativeProcessLinux::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { WriteRegisterSetOperation op(tid, buf, buf_size, regset); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3816,7 +3691,7 @@ log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, GetUnixSignals().GetSignalAsCString (signo)); ResumeOperation op (tid, signo); - DoOperation (&op); + m_monitor_up->DoOperation (&op); if (log) log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, op.GetError().Success() ? "true" : "false"); return op.GetError(); @@ -3971,7 +3846,7 @@ NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) { SingleStepOperation op(tid, signo); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3981,7 +3856,7 @@ NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { SiginfoOperation op(tid, siginfo); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -3989,7 +3864,7 @@ NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) { EventMessageOperation op(tid, message); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -4000,7 +3875,7 @@ return Error(); DetachOperation op(tid); - DoOperation(&op); + m_monitor_up->DoOperation(&op); return op.GetError(); } @@ -4019,9 +3894,9 @@ } void -NativeProcessLinux::StartMonitorThread(Error &error) +NativeProcessLinux::StartMonitorThread(const InitialOperation &initial_operation, Error &error) { - m_monitor_up.reset(new Monitor(GetID(), this)); + m_monitor_up.reset(new Monitor(initial_operation, this)); error = m_monitor_up->Initialize(); if (error.Fail()) { m_monitor_up.reset(); @@ -4033,25 +3908,6 @@ { m_monitor_up.reset(); StopCoordinatorThread (); - StopOpThread(); - sem_destroy(&m_operation_pending); - sem_destroy(&m_operation_done); - - // TODO: validate whether this still holds, fix up comment. - // Note: ProcessPOSIX passes the m_terminal_fd file descriptor to - // Process::SetSTDIOFileDescriptor, which in turn transfers ownership of - // the descriptor to a ConnectionFileDescriptor object. Consequently - // even though still has the file descriptor, we shouldn't close it here. -} - -void -NativeProcessLinux::StopOpThread() -{ - if (!m_operation_thread.IsJoinable()) - return; - - DoOperation(EXIT_OPERATION); - m_operation_thread.Join(nullptr); } Error Index: test/functionalities/thread/concurrent_events/TestConcurrentEvents.py =================================================================== --- test/functionalities/thread/concurrent_events/TestConcurrentEvents.py +++ test/functionalities/thread/concurrent_events/TestConcurrentEvents.py @@ -70,7 +70,6 @@ self.do_thread_actions(num_breakpoint_threads=1, num_signal_threads=1) @dwarf_test - @expectedFailureLinux # this test fails 2/100 dosep runs def test_delay_signal_break_dwarf(self): """Test (1-second delay) signal and a breakpoint in multiple threads.""" self.buildDwarf(dictionary=self.getBuildFlags())