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