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(pos: 0, n: env.find(c: '='));
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(env: arg);
52
53 for (const std::string &entry: m_env_vec) {
54 if (arg_key == GetEnvironmentKey(env: entry))
55 return;
56 }
57 m_env_vec.push_back(x: 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(file: path, buf: &working_directory_stat) != 0) {
70 m_working_directory.clear();
71 return false;
72 }
73 m_working_directory.assign(s: 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(newthread: &m_pid_pthread, NULL,
95 start_routine: ThreadFunctionProcessStatus, arg: 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(mask: 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(mask: 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(mask: RNBContext::event_proc_thread_exiting,
120 timeout_abstime: &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 event_mask: eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged |
173 eEventStdioAvailable | eEventProfileDataAvailable,
174 wait_for_set: 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(mask: 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(mask: 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(mask: 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, event_mask: 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(mask: 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(s: error_num_str, maxlen: sizeof(error_num_str), format: "%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(pid: 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

source code of lldb/tools/debugserver/source/RNBContext.cpp