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