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 | 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* 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 * 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 256 RNBContext::ProcessStateRunning() const 257 { 258 nub_state_t pid_state = DNBProcessGetState(m_pid); 259 return pid_state == eStateRunning || pid_state == eStateStepping; 260 } 261