1*061da546Spatrick //===-- RNBContext.cpp ------------------------------------------*- C++ -*-===// 2*061da546Spatrick // 3*061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*061da546Spatrick // 7*061da546Spatrick //===----------------------------------------------------------------------===// 8*061da546Spatrick // 9*061da546Spatrick // Created by Greg Clayton on 12/12/07. 10*061da546Spatrick // 11*061da546Spatrick //===----------------------------------------------------------------------===// 12*061da546Spatrick 13*061da546Spatrick #include "RNBContext.h" 14*061da546Spatrick 15*061da546Spatrick #include <sstream> 16*061da546Spatrick #include <sys/stat.h> 17*061da546Spatrick 18*061da546Spatrick #if defined(__APPLE__) 19*061da546Spatrick #include <pthread.h> 20*061da546Spatrick #include <sched.h> 21*061da546Spatrick #endif 22*061da546Spatrick 23*061da546Spatrick #include "CFString.h" 24*061da546Spatrick #include "DNB.h" 25*061da546Spatrick #include "DNBLog.h" 26*061da546Spatrick #include "RNBRemote.h" 27*061da546Spatrick 28*061da546Spatrick // Destructor 29*061da546Spatrick RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); } 30*061da546Spatrick 31*061da546Spatrick // RNBContext constructor 32*061da546Spatrick 33*061da546Spatrick const char *RNBContext::EnvironmentAtIndex(size_t index) { 34*061da546Spatrick if (index < m_env_vec.size()) 35*061da546Spatrick return m_env_vec[index].c_str(); 36*061da546Spatrick else 37*061da546Spatrick return NULL; 38*061da546Spatrick } 39*061da546Spatrick 40*061da546Spatrick static std::string GetEnvironmentKey(const std::string &env) { 41*061da546Spatrick std::string key = env.substr(0, env.find('=')); 42*061da546Spatrick if (!key.empty() && key.back() == '=') 43*061da546Spatrick key.pop_back(); 44*061da546Spatrick return key; 45*061da546Spatrick } 46*061da546Spatrick 47*061da546Spatrick void RNBContext::PushEnvironmentIfNeeded(const char *arg) { 48*061da546Spatrick if (!arg) 49*061da546Spatrick return; 50*061da546Spatrick std::string arg_key = GetEnvironmentKey(arg); 51*061da546Spatrick 52*061da546Spatrick for (const std::string &entry: m_env_vec) { 53*061da546Spatrick if (arg_key == GetEnvironmentKey(entry)) 54*061da546Spatrick return; 55*061da546Spatrick } 56*061da546Spatrick m_env_vec.push_back(arg); 57*061da546Spatrick } 58*061da546Spatrick 59*061da546Spatrick const char *RNBContext::ArgumentAtIndex(size_t index) { 60*061da546Spatrick if (index < m_arg_vec.size()) 61*061da546Spatrick return m_arg_vec[index].c_str(); 62*061da546Spatrick else 63*061da546Spatrick return NULL; 64*061da546Spatrick } 65*061da546Spatrick 66*061da546Spatrick bool RNBContext::SetWorkingDirectory(const char *path) { 67*061da546Spatrick struct stat working_directory_stat; 68*061da546Spatrick if (::stat(path, &working_directory_stat) != 0) { 69*061da546Spatrick m_working_directory.clear(); 70*061da546Spatrick return false; 71*061da546Spatrick } 72*061da546Spatrick m_working_directory.assign(path); 73*061da546Spatrick return true; 74*061da546Spatrick } 75*061da546Spatrick 76*061da546Spatrick void RNBContext::SetProcessID(nub_process_t pid) { 77*061da546Spatrick // Delete and events we created 78*061da546Spatrick if (m_pid != INVALID_NUB_PROCESS) { 79*061da546Spatrick StopProcessStatusThread(); 80*061da546Spatrick // Unregister this context as a client of the process's events. 81*061da546Spatrick } 82*061da546Spatrick // Assign our new process ID 83*061da546Spatrick m_pid = pid; 84*061da546Spatrick 85*061da546Spatrick if (pid != INVALID_NUB_PROCESS) { 86*061da546Spatrick StartProcessStatusThread(); 87*061da546Spatrick } 88*061da546Spatrick } 89*061da546Spatrick 90*061da546Spatrick void RNBContext::StartProcessStatusThread() { 91*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); 92*061da546Spatrick if ((m_events.GetEventBits() & event_proc_thread_running) == 0) { 93*061da546Spatrick int err = ::pthread_create(&m_pid_pthread, NULL, 94*061da546Spatrick ThreadFunctionProcessStatus, this); 95*061da546Spatrick if (err == 0) { 96*061da546Spatrick // Our thread was successfully kicked off, wait for it to 97*061da546Spatrick // set the started event so we can safely continue 98*061da546Spatrick m_events.WaitForSetEvents(event_proc_thread_running); 99*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!", 100*061da546Spatrick __FUNCTION__); 101*061da546Spatrick } else { 102*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 103*061da546Spatrick "RNBContext::%s thread failed to start: err = %i", 104*061da546Spatrick __FUNCTION__, err); 105*061da546Spatrick m_events.ResetEvents(event_proc_thread_running); 106*061da546Spatrick m_events.SetEvents(event_proc_thread_exiting); 107*061da546Spatrick } 108*061da546Spatrick } 109*061da546Spatrick } 110*061da546Spatrick 111*061da546Spatrick void RNBContext::StopProcessStatusThread() { 112*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__); 113*061da546Spatrick if ((m_events.GetEventBits() & event_proc_thread_running) == 114*061da546Spatrick event_proc_thread_running) { 115*061da546Spatrick struct timespec timeout_abstime; 116*061da546Spatrick DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0); 117*061da546Spatrick // Wait for 2 seconds for the rx thread to exit 118*061da546Spatrick if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting, 119*061da546Spatrick &timeout_abstime) == 120*061da546Spatrick RNBContext::event_proc_thread_exiting) { 121*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 122*061da546Spatrick "RNBContext::%s thread stopped as requeseted", 123*061da546Spatrick __FUNCTION__); 124*061da546Spatrick } else { 125*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 126*061da546Spatrick "RNBContext::%s thread did not stop in 2 seconds...", 127*061da546Spatrick __FUNCTION__); 128*061da546Spatrick // Kill the RX thread??? 129*061da546Spatrick } 130*061da546Spatrick } 131*061da546Spatrick } 132*061da546Spatrick 133*061da546Spatrick // This thread's sole purpose is to watch for any status changes in the 134*061da546Spatrick // child process. 135*061da546Spatrick void *RNBContext::ThreadFunctionProcessStatus(void *arg) { 136*061da546Spatrick RNBRemoteSP remoteSP(g_remoteSP); 137*061da546Spatrick RNBRemote *remote = remoteSP.get(); 138*061da546Spatrick if (remote == NULL) 139*061da546Spatrick return NULL; 140*061da546Spatrick RNBContext &ctx = remote->Context(); 141*061da546Spatrick 142*061da546Spatrick nub_process_t pid = ctx.ProcessID(); 143*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 144*061da546Spatrick "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...", 145*061da546Spatrick __FUNCTION__, arg, pid); 146*061da546Spatrick ctx.Events().SetEvents(RNBContext::event_proc_thread_running); 147*061da546Spatrick 148*061da546Spatrick #if defined(__APPLE__) 149*061da546Spatrick pthread_setname_np("child process status watcher thread"); 150*061da546Spatrick #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 151*061da546Spatrick struct sched_param thread_param; 152*061da546Spatrick int thread_sched_policy; 153*061da546Spatrick if (pthread_getschedparam(pthread_self(), &thread_sched_policy, 154*061da546Spatrick &thread_param) == 0) { 155*061da546Spatrick thread_param.sched_priority = 47; 156*061da546Spatrick pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); 157*061da546Spatrick } 158*061da546Spatrick #endif 159*061da546Spatrick #endif 160*061da546Spatrick 161*061da546Spatrick bool done = false; 162*061da546Spatrick while (!done) { 163*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 164*061da546Spatrick "RNBContext::%s calling DNBProcessWaitForEvent(pid, " 165*061da546Spatrick "eEventProcessRunningStateChanged | " 166*061da546Spatrick "eEventProcessStoppedStateChanged | eEventStdioAvailable " 167*061da546Spatrick "| eEventProfileDataAvailable, true)...", 168*061da546Spatrick __FUNCTION__); 169*061da546Spatrick nub_event_t pid_status_event = DNBProcessWaitForEvents( 170*061da546Spatrick pid, 171*061da546Spatrick eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | 172*061da546Spatrick eEventStdioAvailable | eEventProfileDataAvailable, 173*061da546Spatrick true, NULL); 174*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 175*061da546Spatrick "RNBContext::%s calling DNBProcessWaitForEvent(pid, " 176*061da546Spatrick "eEventProcessRunningStateChanged | " 177*061da546Spatrick "eEventProcessStoppedStateChanged | eEventStdioAvailable " 178*061da546Spatrick "| eEventProfileDataAvailable, true) => 0x%8.8x", 179*061da546Spatrick __FUNCTION__, pid_status_event); 180*061da546Spatrick 181*061da546Spatrick if (pid_status_event == 0) { 182*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back " 183*061da546Spatrick "from DNBProcessWaitForEvent....", 184*061da546Spatrick __FUNCTION__, pid); 185*061da546Spatrick // done = true; 186*061da546Spatrick } else { 187*061da546Spatrick if (pid_status_event & eEventStdioAvailable) { 188*061da546Spatrick DNBLogThreadedIf( 189*061da546Spatrick LOG_RNB_PROC, 190*061da546Spatrick "RNBContext::%s (pid=%4.4x) got stdio available event....", 191*061da546Spatrick __FUNCTION__, pid); 192*061da546Spatrick ctx.Events().SetEvents(RNBContext::event_proc_stdio_available); 193*061da546Spatrick // Wait for the main thread to consume this notification if it requested 194*061da546Spatrick // we wait for it 195*061da546Spatrick ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available); 196*061da546Spatrick } 197*061da546Spatrick 198*061da546Spatrick if (pid_status_event & eEventProfileDataAvailable) { 199*061da546Spatrick DNBLogThreadedIf( 200*061da546Spatrick LOG_RNB_PROC, 201*061da546Spatrick "RNBContext::%s (pid=%4.4x) got profile data event....", 202*061da546Spatrick __FUNCTION__, pid); 203*061da546Spatrick ctx.Events().SetEvents(RNBContext::event_proc_profile_data); 204*061da546Spatrick // Wait for the main thread to consume this notification if it requested 205*061da546Spatrick // we wait for it 206*061da546Spatrick ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data); 207*061da546Spatrick } 208*061da546Spatrick 209*061da546Spatrick if (pid_status_event & (eEventProcessRunningStateChanged | 210*061da546Spatrick eEventProcessStoppedStateChanged)) { 211*061da546Spatrick nub_state_t pid_state = DNBProcessGetState(pid); 212*061da546Spatrick DNBLogThreadedIf( 213*061da546Spatrick LOG_RNB_PROC, 214*061da546Spatrick "RNBContext::%s (pid=%4.4x) got process state change: %s", 215*061da546Spatrick __FUNCTION__, pid, DNBStateAsString(pid_state)); 216*061da546Spatrick 217*061da546Spatrick // Let the main thread know there is a process state change to see 218*061da546Spatrick ctx.Events().SetEvents(RNBContext::event_proc_state_changed); 219*061da546Spatrick // Wait for the main thread to consume this notification if it requested 220*061da546Spatrick // we wait for it 221*061da546Spatrick ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed); 222*061da546Spatrick 223*061da546Spatrick switch (pid_state) { 224*061da546Spatrick case eStateStopped: 225*061da546Spatrick break; 226*061da546Spatrick 227*061da546Spatrick case eStateInvalid: 228*061da546Spatrick case eStateExited: 229*061da546Spatrick case eStateDetached: 230*061da546Spatrick done = true; 231*061da546Spatrick break; 232*061da546Spatrick default: 233*061da546Spatrick break; 234*061da546Spatrick } 235*061da546Spatrick } 236*061da546Spatrick 237*061da546Spatrick // Reset any events that we consumed. 238*061da546Spatrick DNBProcessResetEvents(pid, pid_status_event); 239*061da546Spatrick } 240*061da546Spatrick } 241*061da546Spatrick DNBLogThreadedIf(LOG_RNB_PROC, 242*061da546Spatrick "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", 243*061da546Spatrick __FUNCTION__, arg, pid); 244*061da546Spatrick ctx.Events().ResetEvents(event_proc_thread_running); 245*061da546Spatrick ctx.Events().SetEvents(event_proc_thread_exiting); 246*061da546Spatrick return NULL; 247*061da546Spatrick } 248*061da546Spatrick 249*061da546Spatrick const char *RNBContext::EventsAsString(nub_event_t events, std::string &s) { 250*061da546Spatrick s.clear(); 251*061da546Spatrick if (events & event_proc_state_changed) 252*061da546Spatrick s += "proc_state_changed "; 253*061da546Spatrick if (events & event_proc_thread_running) 254*061da546Spatrick s += "proc_thread_running "; 255*061da546Spatrick if (events & event_proc_thread_exiting) 256*061da546Spatrick s += "proc_thread_exiting "; 257*061da546Spatrick if (events & event_proc_stdio_available) 258*061da546Spatrick s += "proc_stdio_available "; 259*061da546Spatrick if (events & event_proc_profile_data) 260*061da546Spatrick s += "proc_profile_data "; 261*061da546Spatrick if (events & event_darwin_log_data_available) 262*061da546Spatrick s += "darwin_log_data_available "; 263*061da546Spatrick if (events & event_read_packet_available) 264*061da546Spatrick s += "read_packet_available "; 265*061da546Spatrick if (events & event_read_thread_running) 266*061da546Spatrick s += "read_thread_running "; 267*061da546Spatrick if (events & event_read_thread_running) 268*061da546Spatrick s += "read_thread_running "; 269*061da546Spatrick return s.c_str(); 270*061da546Spatrick } 271*061da546Spatrick 272*061da546Spatrick const char *RNBContext::LaunchStatusAsString(std::string &s) { 273*061da546Spatrick s.clear(); 274*061da546Spatrick 275*061da546Spatrick const char *err_str = m_launch_status.AsString(); 276*061da546Spatrick if (err_str) 277*061da546Spatrick s = err_str; 278*061da546Spatrick else { 279*061da546Spatrick char error_num_str[64]; 280*061da546Spatrick snprintf(error_num_str, sizeof(error_num_str), "%u", 281*061da546Spatrick m_launch_status.Status()); 282*061da546Spatrick s = error_num_str; 283*061da546Spatrick } 284*061da546Spatrick return s.c_str(); 285*061da546Spatrick } 286*061da546Spatrick 287*061da546Spatrick bool RNBContext::ProcessStateRunning() const { 288*061da546Spatrick nub_state_t pid_state = DNBProcessGetState(m_pid); 289*061da546Spatrick return pid_state == eStateRunning || pid_state == eStateStepping; 290*061da546Spatrick } 291