130fdc8d8SChris Lattner //===-- RNBContext.cpp ------------------------------------------*- C++ -*-===//
230fdc8d8SChris Lattner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
630fdc8d8SChris Lattner //
730fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
830fdc8d8SChris Lattner //
930fdc8d8SChris Lattner // Created by Greg Clayton on 12/12/07.
1030fdc8d8SChris Lattner //
1130fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
1230fdc8d8SChris Lattner
1330fdc8d8SChris Lattner #include "RNBContext.h"
146779606aSGreg Clayton
156779606aSGreg Clayton #include <sstream>
16b9c1b51eSKate Stone #include <sys/stat.h>
176779606aSGreg Clayton
1836a216eeSJason Molenda #if defined(__APPLE__)
1936a216eeSJason Molenda #include <pthread.h>
2036a216eeSJason Molenda #include <sched.h>
2136a216eeSJason Molenda #endif
2236a216eeSJason Molenda
23b9c1b51eSKate Stone #include "CFString.h"
2430fdc8d8SChris Lattner #include "DNB.h"
2530fdc8d8SChris Lattner #include "DNBLog.h"
26b9c1b51eSKate Stone #include "RNBRemote.h"
27*bff4673bSJim Ingham #include "MacOSX/MachException.h"
2830fdc8d8SChris Lattner
2930fdc8d8SChris Lattner // Destructor
~RNBContext()30b9c1b51eSKate Stone RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); }
3130fdc8d8SChris Lattner
3230fdc8d8SChris Lattner // RNBContext constructor
3330fdc8d8SChris Lattner
EnvironmentAtIndex(size_t index)34b9c1b51eSKate Stone const char *RNBContext::EnvironmentAtIndex(size_t index) {
3530fdc8d8SChris Lattner if (index < m_env_vec.size())
3630fdc8d8SChris Lattner return m_env_vec[index].c_str();
3730fdc8d8SChris Lattner else
3830fdc8d8SChris Lattner return NULL;
3930fdc8d8SChris Lattner }
4030fdc8d8SChris Lattner
GetEnvironmentKey(const std::string & env)41deb45f20SPavel Labath static std::string GetEnvironmentKey(const std::string &env) {
42deb45f20SPavel Labath std::string key = env.substr(0, env.find('='));
43deb45f20SPavel Labath if (!key.empty() && key.back() == '=')
44deb45f20SPavel Labath key.pop_back();
45deb45f20SPavel Labath return key;
46deb45f20SPavel Labath }
47deb45f20SPavel Labath
PushEnvironmentIfNeeded(const char * arg)48deb45f20SPavel Labath void RNBContext::PushEnvironmentIfNeeded(const char *arg) {
49deb45f20SPavel Labath if (!arg)
50deb45f20SPavel Labath return;
51deb45f20SPavel Labath std::string arg_key = GetEnvironmentKey(arg);
52deb45f20SPavel Labath
53deb45f20SPavel Labath for (const std::string &entry: m_env_vec) {
54deb45f20SPavel Labath if (arg_key == GetEnvironmentKey(entry))
55deb45f20SPavel Labath return;
56deb45f20SPavel Labath }
57deb45f20SPavel Labath m_env_vec.push_back(arg);
58deb45f20SPavel Labath }
59deb45f20SPavel Labath
ArgumentAtIndex(size_t index)60b9c1b51eSKate Stone const char *RNBContext::ArgumentAtIndex(size_t index) {
6130fdc8d8SChris Lattner if (index < m_arg_vec.size())
6230fdc8d8SChris Lattner return m_arg_vec[index].c_str();
6330fdc8d8SChris Lattner else
6430fdc8d8SChris Lattner return NULL;
6530fdc8d8SChris Lattner }
6630fdc8d8SChris Lattner
SetWorkingDirectory(const char * path)67b9c1b51eSKate Stone bool RNBContext::SetWorkingDirectory(const char *path) {
686779606aSGreg Clayton struct stat working_directory_stat;
69b9c1b51eSKate Stone if (::stat(path, &working_directory_stat) != 0) {
706779606aSGreg Clayton m_working_directory.clear();
716779606aSGreg Clayton return false;
726779606aSGreg Clayton }
736779606aSGreg Clayton m_working_directory.assign(path);
746779606aSGreg Clayton return true;
756779606aSGreg Clayton }
766779606aSGreg Clayton
SetProcessID(nub_process_t pid)77b9c1b51eSKate Stone void RNBContext::SetProcessID(nub_process_t pid) {
7830fdc8d8SChris Lattner // Delete and events we created
79b9c1b51eSKate Stone if (m_pid != INVALID_NUB_PROCESS) {
8030fdc8d8SChris Lattner StopProcessStatusThread();
8130fdc8d8SChris Lattner // Unregister this context as a client of the process's events.
8230fdc8d8SChris Lattner }
8330fdc8d8SChris Lattner // Assign our new process ID
8430fdc8d8SChris Lattner m_pid = pid;
8530fdc8d8SChris Lattner
86b9c1b51eSKate Stone if (pid != INVALID_NUB_PROCESS) {
8730fdc8d8SChris Lattner StartProcessStatusThread();
8830fdc8d8SChris Lattner }
8930fdc8d8SChris Lattner }
9030fdc8d8SChris Lattner
StartProcessStatusThread()91b9c1b51eSKate Stone void RNBContext::StartProcessStatusThread() {
9230fdc8d8SChris Lattner DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
93b9c1b51eSKate Stone if ((m_events.GetEventBits() & event_proc_thread_running) == 0) {
94b9c1b51eSKate Stone int err = ::pthread_create(&m_pid_pthread, NULL,
95b9c1b51eSKate Stone ThreadFunctionProcessStatus, this);
96b9c1b51eSKate Stone if (err == 0) {
9730fdc8d8SChris Lattner // Our thread was successfully kicked off, wait for it to
9830fdc8d8SChris Lattner // set the started event so we can safely continue
9930fdc8d8SChris Lattner m_events.WaitForSetEvents(event_proc_thread_running);
100b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!",
101b9c1b51eSKate Stone __FUNCTION__);
102b9c1b51eSKate Stone } else {
103b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
104b9c1b51eSKate Stone "RNBContext::%s thread failed to start: err = %i",
105b9c1b51eSKate Stone __FUNCTION__, err);
10630fdc8d8SChris Lattner m_events.ResetEvents(event_proc_thread_running);
10730fdc8d8SChris Lattner m_events.SetEvents(event_proc_thread_exiting);
10830fdc8d8SChris Lattner }
10930fdc8d8SChris Lattner }
11030fdc8d8SChris Lattner }
11130fdc8d8SChris Lattner
StopProcessStatusThread()112b9c1b51eSKate Stone void RNBContext::StopProcessStatusThread() {
11330fdc8d8SChris Lattner DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
114b9c1b51eSKate Stone if ((m_events.GetEventBits() & event_proc_thread_running) ==
115b9c1b51eSKate Stone event_proc_thread_running) {
11630fdc8d8SChris Lattner struct timespec timeout_abstime;
11730fdc8d8SChris Lattner DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
11830fdc8d8SChris Lattner // Wait for 2 seconds for the rx thread to exit
119b9c1b51eSKate Stone if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting,
120b9c1b51eSKate Stone &timeout_abstime) ==
121b9c1b51eSKate Stone RNBContext::event_proc_thread_exiting) {
122b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
123b9c1b51eSKate Stone "RNBContext::%s thread stopped as requeseted",
124b9c1b51eSKate Stone __FUNCTION__);
125b9c1b51eSKate Stone } else {
126b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
127b9c1b51eSKate Stone "RNBContext::%s thread did not stop in 2 seconds...",
128b9c1b51eSKate Stone __FUNCTION__);
12930fdc8d8SChris Lattner // Kill the RX thread???
13030fdc8d8SChris Lattner }
13130fdc8d8SChris Lattner }
13230fdc8d8SChris Lattner }
13330fdc8d8SChris Lattner
13430fdc8d8SChris Lattner // This thread's sole purpose is to watch for any status changes in the
13530fdc8d8SChris Lattner // child process.
ThreadFunctionProcessStatus(void * arg)136b9c1b51eSKate Stone void *RNBContext::ThreadFunctionProcessStatus(void *arg) {
13730fdc8d8SChris Lattner RNBRemoteSP remoteSP(g_remoteSP);
13830fdc8d8SChris Lattner RNBRemote *remote = remoteSP.get();
13930fdc8d8SChris Lattner if (remote == NULL)
14030fdc8d8SChris Lattner return NULL;
14130fdc8d8SChris Lattner RNBContext &ctx = remote->Context();
14230fdc8d8SChris Lattner
14330fdc8d8SChris Lattner nub_process_t pid = ctx.ProcessID();
144b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
145b9c1b51eSKate Stone "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...",
146b9c1b51eSKate Stone __FUNCTION__, arg, pid);
14730fdc8d8SChris Lattner ctx.Events().SetEvents(RNBContext::event_proc_thread_running);
14836a216eeSJason Molenda
14936a216eeSJason Molenda #if defined(__APPLE__)
15036a216eeSJason Molenda pthread_setname_np("child process status watcher thread");
15136a216eeSJason Molenda #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
15236a216eeSJason Molenda struct sched_param thread_param;
15336a216eeSJason Molenda int thread_sched_policy;
154b9c1b51eSKate Stone if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
155b9c1b51eSKate Stone &thread_param) == 0) {
15636a216eeSJason Molenda thread_param.sched_priority = 47;
15736a216eeSJason Molenda pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
15836a216eeSJason Molenda }
15936a216eeSJason Molenda #endif
16036a216eeSJason Molenda #endif
16136a216eeSJason Molenda
16230fdc8d8SChris Lattner bool done = false;
163b9c1b51eSKate Stone while (!done) {
164b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
165b9c1b51eSKate Stone "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
166b9c1b51eSKate Stone "eEventProcessRunningStateChanged | "
167b9c1b51eSKate Stone "eEventProcessStoppedStateChanged | eEventStdioAvailable "
168b9c1b51eSKate Stone "| eEventProfileDataAvailable, true)...",
169b9c1b51eSKate Stone __FUNCTION__);
170b9c1b51eSKate Stone nub_event_t pid_status_event = DNBProcessWaitForEvents(
171b9c1b51eSKate Stone pid,
172b9c1b51eSKate Stone eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged |
173b9c1b51eSKate Stone eEventStdioAvailable | eEventProfileDataAvailable,
174b9c1b51eSKate Stone true, NULL);
175b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
176b9c1b51eSKate Stone "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
177b9c1b51eSKate Stone "eEventProcessRunningStateChanged | "
178b9c1b51eSKate Stone "eEventProcessStoppedStateChanged | eEventStdioAvailable "
179b9c1b51eSKate Stone "| eEventProfileDataAvailable, true) => 0x%8.8x",
180b9c1b51eSKate Stone __FUNCTION__, pid_status_event);
18130fdc8d8SChris Lattner
182b9c1b51eSKate Stone if (pid_status_event == 0) {
183b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back "
184b9c1b51eSKate Stone "from DNBProcessWaitForEvent....",
185b9c1b51eSKate Stone __FUNCTION__, pid);
18630fdc8d8SChris Lattner // done = true;
187b9c1b51eSKate Stone } else {
188b9c1b51eSKate Stone if (pid_status_event & eEventStdioAvailable) {
189b9c1b51eSKate Stone DNBLogThreadedIf(
190b9c1b51eSKate Stone LOG_RNB_PROC,
191b9c1b51eSKate Stone "RNBContext::%s (pid=%4.4x) got stdio available event....",
192b9c1b51eSKate Stone __FUNCTION__, pid);
19330fdc8d8SChris Lattner ctx.Events().SetEvents(RNBContext::event_proc_stdio_available);
194b9c1b51eSKate Stone // Wait for the main thread to consume this notification if it requested
195b9c1b51eSKate Stone // we wait for it
19630fdc8d8SChris Lattner ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available);
19730fdc8d8SChris Lattner }
19830fdc8d8SChris Lattner
199b9c1b51eSKate Stone if (pid_status_event & eEventProfileDataAvailable) {
200b9c1b51eSKate Stone DNBLogThreadedIf(
201b9c1b51eSKate Stone LOG_RNB_PROC,
202b9c1b51eSKate Stone "RNBContext::%s (pid=%4.4x) got profile data event....",
203b9c1b51eSKate Stone __FUNCTION__, pid);
204ab3b8b22SHan Ming Ong ctx.Events().SetEvents(RNBContext::event_proc_profile_data);
205b9c1b51eSKate Stone // Wait for the main thread to consume this notification if it requested
206b9c1b51eSKate Stone // we wait for it
207ab3b8b22SHan Ming Ong ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data);
208ab3b8b22SHan Ming Ong }
20930fdc8d8SChris Lattner
210b9c1b51eSKate Stone if (pid_status_event & (eEventProcessRunningStateChanged |
211b9c1b51eSKate Stone eEventProcessStoppedStateChanged)) {
21230fdc8d8SChris Lattner nub_state_t pid_state = DNBProcessGetState(pid);
213b9c1b51eSKate Stone DNBLogThreadedIf(
214b9c1b51eSKate Stone LOG_RNB_PROC,
215b9c1b51eSKate Stone "RNBContext::%s (pid=%4.4x) got process state change: %s",
216b9c1b51eSKate Stone __FUNCTION__, pid, DNBStateAsString(pid_state));
21730fdc8d8SChris Lattner
21830fdc8d8SChris Lattner // Let the main thread know there is a process state change to see
21930fdc8d8SChris Lattner ctx.Events().SetEvents(RNBContext::event_proc_state_changed);
220b9c1b51eSKate Stone // Wait for the main thread to consume this notification if it requested
221b9c1b51eSKate Stone // we wait for it
22230fdc8d8SChris Lattner ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed);
22330fdc8d8SChris Lattner
224b9c1b51eSKate Stone switch (pid_state) {
22530fdc8d8SChris Lattner case eStateStopped:
22630fdc8d8SChris Lattner break;
22730fdc8d8SChris Lattner
22830fdc8d8SChris Lattner case eStateInvalid:
22930fdc8d8SChris Lattner case eStateExited:
23058d1c9a4SGreg Clayton case eStateDetached:
23130fdc8d8SChris Lattner done = true;
23230fdc8d8SChris Lattner break;
233effe5c95SGreg Clayton default:
234effe5c95SGreg Clayton break;
23530fdc8d8SChris Lattner }
23630fdc8d8SChris Lattner }
23730fdc8d8SChris Lattner
23830fdc8d8SChris Lattner // Reset any events that we consumed.
23930fdc8d8SChris Lattner DNBProcessResetEvents(pid, pid_status_event);
24030fdc8d8SChris Lattner }
24130fdc8d8SChris Lattner }
242b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_PROC,
243b9c1b51eSKate Stone "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...",
244b9c1b51eSKate Stone __FUNCTION__, arg, pid);
24530fdc8d8SChris Lattner ctx.Events().ResetEvents(event_proc_thread_running);
24630fdc8d8SChris Lattner ctx.Events().SetEvents(event_proc_thread_exiting);
24730fdc8d8SChris Lattner return NULL;
24830fdc8d8SChris Lattner }
24930fdc8d8SChris Lattner
EventsAsString(nub_event_t events,std::string & s)250b9c1b51eSKate Stone const char *RNBContext::EventsAsString(nub_event_t events, std::string &s) {
25130fdc8d8SChris Lattner s.clear();
25230fdc8d8SChris Lattner if (events & event_proc_state_changed)
25330fdc8d8SChris Lattner s += "proc_state_changed ";
25430fdc8d8SChris Lattner if (events & event_proc_thread_running)
25530fdc8d8SChris Lattner s += "proc_thread_running ";
25630fdc8d8SChris Lattner if (events & event_proc_thread_exiting)
25730fdc8d8SChris Lattner s += "proc_thread_exiting ";
25830fdc8d8SChris Lattner if (events & event_proc_stdio_available)
25930fdc8d8SChris Lattner s += "proc_stdio_available ";
260ab3b8b22SHan Ming Ong if (events & event_proc_profile_data)
261ab3b8b22SHan Ming Ong s += "proc_profile_data ";
26230fdc8d8SChris Lattner if (events & event_read_packet_available)
26330fdc8d8SChris Lattner s += "read_packet_available ";
26430fdc8d8SChris Lattner if (events & event_read_thread_running)
26530fdc8d8SChris Lattner s += "read_thread_running ";
26630fdc8d8SChris Lattner if (events & event_read_thread_running)
26730fdc8d8SChris Lattner s += "read_thread_running ";
26830fdc8d8SChris Lattner return s.c_str();
26930fdc8d8SChris Lattner }
27030fdc8d8SChris Lattner
LaunchStatusAsString(std::string & s)271b9c1b51eSKate Stone const char *RNBContext::LaunchStatusAsString(std::string &s) {
27230fdc8d8SChris Lattner s.clear();
27330fdc8d8SChris Lattner
27430fdc8d8SChris Lattner const char *err_str = m_launch_status.AsString();
27530fdc8d8SChris Lattner if (err_str)
27630fdc8d8SChris Lattner s = err_str;
277b9c1b51eSKate Stone else {
27830fdc8d8SChris Lattner char error_num_str[64];
279b9c1b51eSKate Stone snprintf(error_num_str, sizeof(error_num_str), "%u",
28097206d57SZachary Turner m_launch_status.Status());
28130fdc8d8SChris Lattner s = error_num_str;
28230fdc8d8SChris Lattner }
28330fdc8d8SChris Lattner return s.c_str();
28430fdc8d8SChris Lattner }
28530fdc8d8SChris Lattner
ProcessStateRunning() const286b9c1b51eSKate Stone bool RNBContext::ProcessStateRunning() const {
28730fdc8d8SChris Lattner nub_state_t pid_state = DNBProcessGetState(m_pid);
28830fdc8d8SChris Lattner return pid_state == eStateRunning || pid_state == eStateStepping;
28930fdc8d8SChris Lattner }
290*bff4673bSJim Ingham
AddIgnoredException(const char * exception_name)291*bff4673bSJim Ingham bool RNBContext::AddIgnoredException(const char *exception_name) {
292*bff4673bSJim Ingham exception_mask_t exc_mask = MachException::ExceptionMask(exception_name);
293*bff4673bSJim Ingham if (exc_mask == 0)
294*bff4673bSJim Ingham return false;
295*bff4673bSJim Ingham m_ignored_exceptions.push_back(exc_mask);
296*bff4673bSJim Ingham return true;
297*bff4673bSJim Ingham }
298*bff4673bSJim Ingham
AddDefaultIgnoredExceptions()299*bff4673bSJim Ingham void RNBContext::AddDefaultIgnoredExceptions() {
300*bff4673bSJim Ingham m_ignored_exceptions.push_back(EXC_MASK_BAD_ACCESS);
301*bff4673bSJim Ingham m_ignored_exceptions.push_back(EXC_MASK_BAD_INSTRUCTION);
302*bff4673bSJim Ingham m_ignored_exceptions.push_back(EXC_MASK_ARITHMETIC);
303*bff4673bSJim Ingham }
304