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 //----------------------------------------------------------------------
~RNBContext()28 RNBContext::~RNBContext()
29 {
30     SetProcessID (INVALID_NUB_PROCESS);
31 }
32 
33 //----------------------------------------------------------------------
34 // RNBContext constructor
35 //----------------------------------------------------------------------
36 
37 const char *
EnvironmentAtIndex(int index)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 *
ArgumentAtIndex(int index)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
SetWorkingDirectory(const char * path)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
SetProcessID(nub_process_t pid)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
StartProcessStatusThread()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
StopProcessStatusThread()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*
ThreadFunctionProcessStatus(void * arg)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 | eEventProfileDataAvailable, true)...", __FUNCTION__);
152         nub_event_t pid_status_event = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, true, NULL);
153         DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s calling DNBProcessWaitForEvent(pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventStdioAvailable | eEventProfileDataAvailable, 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             if (pid_status_event & eEventProfileDataAvailable)
171             {
172                 DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got profile data event....", __FUNCTION__, pid);
173                 ctx.Events().SetEvents (RNBContext::event_proc_profile_data);
174                 // Wait for the main thread to consume this notification if it requested we wait for it
175                 ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data);
176             }
177 
178             if (pid_status_event & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
179             {
180                 nub_state_t pid_state = DNBProcessGetState(pid);
181                 DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got process state change: %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
182 
183                 // Let the main thread know there is a process state change to see
184                 ctx.Events().SetEvents (RNBContext::event_proc_state_changed);
185                 // Wait for the main thread to consume this notification if it requested we wait for it
186                 ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed);
187 
188                 switch (pid_state)
189                 {
190                 case eStateStopped:
191                     break;
192 
193                 case eStateInvalid:
194                 case eStateExited:
195                 case eStateDetached:
196                     done = true;
197                     break;
198                 default:
199                     break;
200                 }
201             }
202 
203             // Reset any events that we consumed.
204             DNBProcessResetEvents(pid, pid_status_event);
205 
206         }
207     }
208     DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", __FUNCTION__, arg, pid);
209     ctx.Events().ResetEvents(event_proc_thread_running);
210     ctx.Events().SetEvents(event_proc_thread_exiting);
211     return NULL;
212 }
213 
214 
215 const char*
EventsAsString(nub_event_t events,std::string & s)216 RNBContext::EventsAsString (nub_event_t events, std::string& s)
217 {
218     s.clear();
219     if (events & event_proc_state_changed)
220         s += "proc_state_changed ";
221     if (events & event_proc_thread_running)
222         s += "proc_thread_running ";
223     if (events & event_proc_thread_exiting)
224         s += "proc_thread_exiting ";
225     if (events & event_proc_stdio_available)
226         s += "proc_stdio_available ";
227     if (events & event_proc_profile_data)
228         s += "proc_profile_data ";
229     if (events & event_read_packet_available)
230         s += "read_packet_available ";
231     if (events & event_read_thread_running)
232         s += "read_thread_running ";
233     if (events & event_read_thread_running)
234         s += "read_thread_running ";
235     return s.c_str();
236 }
237 
238 const char *
LaunchStatusAsString(std::string & s)239 RNBContext::LaunchStatusAsString (std::string& s)
240 {
241     s.clear();
242 
243     const char *err_str = m_launch_status.AsString();
244     if (err_str)
245         s = err_str;
246     else
247     {
248         char error_num_str[64];
249         snprintf(error_num_str, sizeof(error_num_str), "%u", m_launch_status.Error());
250         s = error_num_str;
251     }
252     return s.c_str();
253 }
254 
255 bool
ProcessStateRunning() const256 RNBContext::ProcessStateRunning() const
257 {
258     nub_state_t pid_state = DNBProcessGetState(m_pid);
259     return pid_state == eStateRunning || pid_state == eStateStepping;
260 }
261