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