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