304 lines · cpp
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-exception6//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#endif22 23#include "CFString.h"24#include "DNB.h"25#include "DNBLog.h"26#include "RNBRemote.h"27#include "MacOSX/MachException.h"28 29// Destructor30RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); }31 32// RNBContext constructor33 34const char *RNBContext::EnvironmentAtIndex(size_t index) {35 if (index < m_env_vec.size())36 return m_env_vec[index].c_str();37 else38 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 else64 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 created79 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 ID84 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 to98 // set the started event so we can safely continue99 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 exit119 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 the135// 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#endif160#endif161 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 requested195 // we wait for it196 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 requested206 // we wait for it207 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 see219 ctx.Events().SetEvents(RNBContext::event_proc_state_changed);220 // Wait for the main thread to consume this notification if it requested221 // we wait for it222 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