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