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