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