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