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