debugserver process launch, attach, stop, and resume
The process-level state machine that launches or attaches to a debuggee, bundles received exceptions into a stop, replies to outstanding exceptions, and resumes selected threads.
MachProcess::Resume source
bool MachProcess::Resume(const DNBThreadResumeActions &thread_actions) {
DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
nub_state_t state = GetState();
if (CanResume(state)) {
m_thread_actions = thread_actions;
PrivateResume();
return true;
} else if (state == eStateRunning) {
DNBLog("Resume() - task 0x%x is already running, ignoring...",
m_task.TaskPort());
return true;
}
DNBLog("Resume() - task 0x%x has state %s, can't continue...",
m_task.TaskPort(), DNBStateAsString(state));
return false;
}MachProcess::ReplyToAllExceptions source
void MachProcess::ReplyToAllExceptions() {
std::lock_guard<std::recursive_mutex> guard(m_exception_and_signal_mutex);
if (!m_exception_messages.empty()) {
MachException::Message::iterator pos;
MachException::Message::iterator begin = m_exception_messages.begin();
MachException::Message::iterator end = m_exception_messages.end();
for (pos = begin; pos != end; ++pos) {
DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...",
(uint32_t)std::distance(begin, pos));
int thread_reply_signal = 0;
nub_thread_t tid =
m_thread_list.GetThreadIDByMachPortNumber(pos->state.thread_port);
const DNBThreadResumeAction *action = NULL;
if (tid != INVALID_NUB_THREAD) {
action = m_thread_actions.GetActionForThread(tid, false);
}
if (action) {
thread_reply_signal = action->signal;
if (thread_reply_signal)
m_thread_actions.SetSignalHandledForThread(tid);
}
DNBError err(pos->Reply(this, thread_reply_signal));
if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
err.LogThreadedIfError("Error replying to exception");
}
// Erase all exception message as we should have used and replied
… more in sourceMachProcess::PrivateResume source
void MachProcess::PrivateResume() {
std::lock_guard<std::recursive_mutex> guard(m_exception_and_signal_mutex);
m_auto_resume_signo = m_sent_interrupt_signo;
if (m_auto_resume_signo)
DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x "
"resuming (with unhandled interrupt signal "
"%i)...",
m_task.TaskPort(), m_auto_resume_signo);
else
DNBLogThreadedIf(LOG_PROCESS,
"MachProcess::PrivateResume() - task 0x%x resuming...",
m_task.TaskPort());
ReplyToAllExceptions();
// bool stepOverBreakInstruction = step;
// Let the thread prepare to resume and see if any threads want us to
// step over a breakpoint instruction (ProcessWillResume will modify
// the value of stepOverBreakInstruction).
m_thread_list.ProcessWillResume(this, m_thread_actions);
// Set our state accordingly
if (m_thread_actions.NumActionsWithState(eStateStepping))
SetState(eStateStepping);
else
SetState(eStateRunning);
// Now resume our task.
m_task.Resume();
… more in sourceMachProcess::ExceptionMessageReceived source
Called by the exception thread when an exception has been received from
our process. The exception message is completely filled and the exception
data has already been copied.
void MachProcess::ExceptionMessageReceived(
const MachException::Message &exceptionMessage) {
std::lock_guard<std::recursive_mutex> guard(m_exception_and_signal_mutex);
if (m_exception_messages.empty())
m_task.Suspend();
DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
// Use a locker to automatically unlock our mutex in case of exceptions
// Add the exception to our internal exception stack
m_exception_messages.push_back(exceptionMessage);
}MachProcess::ExceptionMessageBundleComplete source
task_t MachProcess::ExceptionMessageBundleComplete() { // We have a complete bundle of exceptions for our child process. std::lock_guard<std::recursive_mutex> guard(m_exception_and_signal_mutex); DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); bool auto_resume = false; if (!m_exception_messages.empty()) { m_did_exec = false; // First check for any SIGTRAP and make sure we didn't exec const task_t task = m_task.TaskPort(); size_t i; if (m_pid != 0) { bool received_interrupt = false; uint32_t num_task_exceptions = 0; for (i = 0; i < m_exception_messages.size(); ++i) { if (m_exception_messages[i].state.task_port == task) { ++num_task_exceptions; const int signo = m_exception_messages[i].state.SoftSignal(); if (signo == SIGTRAP) { // SIGTRAP could mean that we exec'ed. We need to check the // dyld all_image_infos.infoArray to see if it is NULL and if // so, say that we exec'ed. const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress(); if (aii_addr != INVALID_NUB_ADDRESS) { const nub_addr_t info_array_count_addr = aii_addr + 4; uint32_t info_array_count = 0; if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4) { if (info_array_count == 0) { m_did_exec = true; … more in source
MachProcess::AttachForDebug source
namespace
pid_t MachProcess::AttachForDebug( pid_t pid, const RNBContext::IgnoredExceptions &ignored_exceptions, char *err_str, size_t err_len) { // Clear out and clean up from any current state Clear(); if (pid != 0) { DNBError err; // Make sure the process exists... if (::getpgid(pid) < 0) { err.SetErrorToErrno(); const char *err_cstr = err.AsString(); ::snprintf(err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); DNBLogError ("MachProcess::AttachForDebug pid %d does not exist", pid); return INVALID_NUB_PROCESS; } SetState(eStateAttaching); m_pid = pid; if (!m_task.StartExceptionThread(ignored_exceptions, err)) { const char *err_cstr = err.AsString(); ::snprintf(err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); DNBLogError( "[LaunchAttach] END (%d) MachProcess::AttachForDebug failed to start " "exception thread attaching to pid %i: %s", getpid(), pid, err_str); m_pid = INVALID_NUB_PROCESS; return INVALID_NUB_PROCESS; … more in source
MachProcess::PosixSpawnChildForPTraceDebugging source
pid_t MachProcess::PosixSpawnChildForPTraceDebugging( const char *path, cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, char const *argv[], char const *envp[], const char *working_directory, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio, MachProcess *process, int disable_aslr, DNBError &err) { posix_spawnattr_t attr; short flags; DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, " "working_dir=%s, stdin=%s, stdout=%s " "stderr=%s, no-stdio=%i)", __FUNCTION__, path, static_cast<const void *>(argv), static_cast<const void *>(envp), working_directory, stdin_path, stdout_path, stderr_path, no_stdio); err.SetError(::posix_spawnattr_init(&attr), DNBError::POSIX); if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) err.LogThreaded("::posix_spawnattr_init ( &attr )"); if (err.Fail()) return INVALID_NUB_PROCESS; flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETPGROUP; if (disable_aslr) flags |= _POSIX_SPAWN_DISABLE_ASLR; sigset_t no_signals; sigset_t all_signals; sigemptyset(&no_signals); sigfillset(&all_signals); … more in source