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