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
~RNBContext()29 RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); }
30
31 // RNBContext constructor
32
EnvironmentAtIndex(size_t index)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
GetEnvironmentKey(const std::string & env)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
PushEnvironmentIfNeeded(const char * arg)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
ArgumentAtIndex(size_t index)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
SetWorkingDirectory(const char * path)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
SetProcessID(nub_process_t pid)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
StartProcessStatusThread()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
StopProcessStatusThread()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.
ThreadFunctionProcessStatus(void * arg)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
EventsAsString(nub_event_t events,std::string & s)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
LaunchStatusAsString(std::string & s)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
ProcessStateRunning() const287 bool RNBContext::ProcessStateRunning() const {
288 nub_state_t pid_state = DNBProcessGetState(m_pid);
289 return pid_state == eStateRunning || pid_state == eStateStepping;
290 }
291