debugserver task control and exception thread
LLDB debugserver’s task-port acquisition, task suspension, exception-port save/restore, and exception receive thread. This is the closest upstream implementation comparison for machdb’s launch, attach, stop, continue, and detach lifecycle.
MachTask::Suspend source
----------------------------------------------------------------------
MachTask::Suspend
----------------------------------------------------------------------
kern_return_t MachTask::Suspend() { DNBError err; task_t task = TaskPort(); err = ::task_suspend(task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task); return err.Status(); }
MachTask::Resume source
----------------------------------------------------------------------
MachTask::Resume
----------------------------------------------------------------------
kern_return_t MachTask::Resume() { struct task_basic_info task_info; task_t task = TaskPort(); if (task == TASK_NULL) return KERN_INVALID_ARGUMENT; DNBError err; err = BasicInfo(task, &task_info); if (err.Success()) { if (m_do_double_resume && task_info.suspend_count == 2) { err = ::task_resume(task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_resume double-resume after exec-start-stopped " "( target_task = 0x%4.4x )", task); } m_do_double_resume = false; // task_resume isn't counted like task_suspend calls are, are, so if the // task is not suspended, don't try and resume it since it is already // running if (task_info.suspend_count > 0) { err = ::task_resume(task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task); } } return err.Status(); }
MachTask::SaveExceptionPortInfo source
----------------------------------------------------------------------
MachTask::SaveExceptionPortInfo
----------------------------------------------------------------------
kern_return_t MachTask::SaveExceptionPortInfo() { return m_exc_port_info.Save(TaskPort()); }
MachTask::RestoreExceptionPortInfo source
----------------------------------------------------------------------
MachTask::RestoreExceptionPortInfo
----------------------------------------------------------------------
kern_return_t MachTask::RestoreExceptionPortInfo() { return m_exc_port_info.Restore(TaskPort()); }
MachTask::TaskPortForProcessID source
----------------------------------------------------------------------
MachTask::TaskPortForProcessID
----------------------------------------------------------------------
task_t MachTask::TaskPortForProcessID(DNBError &err, bool force) { if (((m_task == TASK_NULL) || force) && m_process != NULL) m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err); return m_task; }
MachTask::StartExceptionThread source
bool MachTask::StartExceptionThread(
const RNBContext::IgnoredExceptions &ignored_exceptions,
DNBError &err) {
DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
task_t task = TaskPortForProcessID(err);
if (MachTask::IsValid(task)) {
// Got the mach port for the current process
mach_port_t task_self = mach_task_self();
// Allocate an exception port that we will use to track our child process
err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
&m_exception_port);
if (err.Fail())
return false;
// Add the ability to send messages on the new exception port
err = ::mach_port_insert_right(task_self, m_exception_port,
m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
if (err.Fail())
return false;
// Save the original state of the exception ports for our child process
SaveExceptionPortInfo();
// We weren't able to save the info for our exception ports, we must stop...
if (m_exc_port_info.mask == 0) {
err.SetErrorString("failed to get exception port info");
return false;
}
… more in sourceMachTask::ShutDownExceptionThread source
void MachTask::ShutDownExceptionThread() {
DNBError err;
if (!m_exception_thread)
return;
err = RestoreExceptionPortInfo();
// NULL our exception port and let our exception thread exit
mach_port_t exception_port = m_exception_port;
m_exception_port = 0;
err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)",
m_exception_thread);
m_exception_thread = nullptr;
// Deallocate our exception port that we used to track our child process
mach_port_t task_self = mach_task_self();
err = ::mach_port_deallocate(task_self, exception_port);
if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )",
task_self, exception_port);
… more in sourceMachTask::ExceptionThread source
void *MachTask::ExceptionThread(void *arg) {
if (arg == NULL)
return NULL;
MachTask *mach_task = (MachTask *)arg;
MachProcess *mach_proc = mach_task->Process();
DNBLogThreadedIf(LOG_EXCEPTIONS,
"MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__,
arg);
#if defined(__APPLE__)
pthread_setname_np("exception monitoring thread");
#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
struct sched_param thread_param;
int thread_sched_policy;
if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
&thread_param) == 0) {
thread_param.sched_priority = 47;
pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
}
#endif
#endif
// We keep a count of the number of consecutive exceptions received so
// we know to grab all exceptions without a timeout. We do this to get a
// bunch of related exceptions on our exception port so we can process
// then together. When we have multiple threads, we can get an exception
// per thread and they will come in consecutively. The main loop in this
// thread can stop periodically if needed to service things related to this
// process.
… more in source