//===-- RNBContext.cpp ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Created by Greg Clayton on 12/12/07. // //===----------------------------------------------------------------------===// #include "RNBContext.h" #include #include #if defined(__APPLE__) #include #include #endif #include "CFString.h" #include "DNB.h" #include "DNBLog.h" #include "RNBRemote.h" //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); } //---------------------------------------------------------------------- // RNBContext constructor //---------------------------------------------------------------------- const char *RNBContext::EnvironmentAtIndex(size_t index) { if (index < m_env_vec.size()) return m_env_vec[index].c_str(); else return NULL; } static std::string GetEnvironmentKey(const std::string &env) { std::string key = env.substr(0, env.find('=')); if (!key.empty() && key.back() == '=') key.pop_back(); return key; } void RNBContext::PushEnvironmentIfNeeded(const char *arg) { if (!arg) return; std::string arg_key = GetEnvironmentKey(arg); for (const std::string &entry: m_env_vec) { if (arg_key == GetEnvironmentKey(entry)) return; } m_env_vec.push_back(arg); } const char *RNBContext::ArgumentAtIndex(size_t index) { if (index < m_arg_vec.size()) return m_arg_vec[index].c_str(); else return NULL; } bool RNBContext::SetWorkingDirectory(const char *path) { struct stat working_directory_stat; if (::stat(path, &working_directory_stat) != 0) { m_working_directory.clear(); return false; } m_working_directory.assign(path); return true; } void RNBContext::SetProcessID(nub_process_t pid) { // Delete and events we created if (m_pid != INVALID_NUB_PROCESS) { StopProcessStatusThread(); // Unregister this context as a client of the process's events. } // Assign our new process ID m_pid = pid; if (pid != INVALID_NUB_PROCESS) { StartProcessStatusThread(); } } void RNBContext::StartProcessStatusThread() { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); if ((m_events.GetEventBits() & event_proc_thread_running) == 0) { int err = ::pthread_create(&m_pid_pthread, NULL, ThreadFunctionProcessStatus, this); if (err == 0) { // Our thread was successfully kicked off, wait for it to // set the started event so we can safely continue m_events.WaitForSetEvents(event_proc_thread_running); DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!", __FUNCTION__); } else { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread failed to start: err = %i", __FUNCTION__, err); m_events.ResetEvents(event_proc_thread_running); m_events.SetEvents(event_proc_thread_exiting); } } } void RNBContext::StopProcessStatusThread() { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); if ((m_events.GetEventBits() & event_proc_thread_running) == event_proc_thread_running) { struct timespec timeout_abstime; DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); // Wait for 2 seconds for the rx thread to exit if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting, &timeout_abstime) == RNBContext::event_proc_thread_exiting) { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread stopped as requeseted", __FUNCTION__); } else { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread did not stop in 2 seconds...", __FUNCTION__); // Kill the RX thread??? } } } //---------------------------------------------------------------------- // This thread's sole purpose is to watch for any status changes in the // child process. //---------------------------------------------------------------------- void *RNBContext::ThreadFunctionProcessStatus(void *arg) { RNBRemoteSP remoteSP(g_remoteSP); RNBRemote *remote = remoteSP.get(); if (remote == NULL) return NULL; RNBContext &ctx = remote->Context(); nub_process_t pid = ctx.ProcessID(); DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...", __FUNCTION__, arg, pid); ctx.Events().SetEvents(RNBContext::event_proc_thread_running); #if defined(__APPLE__) pthread_setname_np("child process status watcher 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 bool done = false; while (!done) { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, " "eEventProcessRunningStateChanged | " "eEventProcessStoppedStateChanged | eEventStdioAvailable " "| eEventProfileDataAvailable, true)...", __FUNCTION__); nub_event_t pid_status_event = DNBProcessWaitForEvents( pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true, NULL); DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, " "eEventProcessRunningStateChanged | " "eEventProcessStoppedStateChanged | eEventStdioAvailable " "| eEventProfileDataAvailable, true) => 0x%8.8x", __FUNCTION__, pid_status_event); if (pid_status_event == 0) { DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back " "from DNBProcessWaitForEvent....", __FUNCTION__, pid); // done = true; } else { if (pid_status_event & eEventStdioAvailable) { DNBLogThreadedIf( LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got stdio available event....", __FUNCTION__, pid); ctx.Events().SetEvents(RNBContext::event_proc_stdio_available); // Wait for the main thread to consume this notification if it requested // we wait for it ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available); } if (pid_status_event & eEventProfileDataAvailable) { DNBLogThreadedIf( LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got profile data event....", __FUNCTION__, pid); ctx.Events().SetEvents(RNBContext::event_proc_profile_data); // Wait for the main thread to consume this notification if it requested // we wait for it ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data); } if (pid_status_event & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) { nub_state_t pid_state = DNBProcessGetState(pid); DNBLogThreadedIf( LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got process state change: %s", __FUNCTION__, pid, DNBStateAsString(pid_state)); // Let the main thread know there is a process state change to see ctx.Events().SetEvents(RNBContext::event_proc_state_changed); // Wait for the main thread to consume this notification if it requested // we wait for it ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed); switch (pid_state) { case eStateStopped: break; case eStateInvalid: case eStateExited: case eStateDetached: done = true; break; default: break; } } // Reset any events that we consumed. DNBProcessResetEvents(pid, pid_status_event); } } DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", __FUNCTION__, arg, pid); ctx.Events().ResetEvents(event_proc_thread_running); ctx.Events().SetEvents(event_proc_thread_exiting); return NULL; } const char *RNBContext::EventsAsString(nub_event_t events, std::string &s) { s.clear(); if (events & event_proc_state_changed) s += "proc_state_changed "; if (events & event_proc_thread_running) s += "proc_thread_running "; if (events & event_proc_thread_exiting) s += "proc_thread_exiting "; if (events & event_proc_stdio_available) s += "proc_stdio_available "; if (events & event_proc_profile_data) s += "proc_profile_data "; if (events & event_darwin_log_data_available) s += "darwin_log_data_available "; if (events & event_read_packet_available) s += "read_packet_available "; if (events & event_read_thread_running) s += "read_thread_running "; if (events & event_read_thread_running) s += "read_thread_running "; return s.c_str(); } const char *RNBContext::LaunchStatusAsString(std::string &s) { s.clear(); const char *err_str = m_launch_status.AsString(); if (err_str) s = err_str; else { char error_num_str[64]; snprintf(error_num_str, sizeof(error_num_str), "%u", m_launch_status.Status()); s = error_num_str; } return s.c_str(); } bool RNBContext::ProcessStateRunning() const { nub_state_t pid_state = DNBProcessGetState(m_pid); return pid_state == eStateRunning || pid_state == eStateStepping; }