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