Chris Lattner | 30fdc8d | 2010-06-08 16:52:24 +0000 | [diff] [blame] | 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" |
Greg Clayton | 6779606a | 2011-01-22 23:43:18 +0000 | [diff] [blame^] | 15 | |
| 16 | #include <sys/stat.h> |
| 17 | #include <sstream> |
| 18 | |
Chris Lattner | 30fdc8d | 2010-06-08 16:52:24 +0000 | [diff] [blame] | 19 | #include "RNBRemote.h" |
| 20 | #include "DNB.h" |
| 21 | #include "DNBLog.h" |
| 22 | #include "CFString.h" |
Greg Clayton | 6779606a | 2011-01-22 23:43:18 +0000 | [diff] [blame^] | 23 | |
Chris Lattner | 30fdc8d | 2010-06-08 16:52:24 +0000 | [diff] [blame] | 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 | |
Greg Clayton | 6779606a | 2011-01-22 23:43:18 +0000 | [diff] [blame^] | 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 | |
Chris Lattner | 30fdc8d | 2010-06-08 16:52:24 +0000 | [diff] [blame] | 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: |
Greg Clayton | 58d1c9a | 2010-10-18 04:14:23 +0000 | [diff] [blame] | 188 | case eStateDetached: |
Chris Lattner | 30fdc8d | 2010-06-08 16:52:24 +0000 | [diff] [blame] | 189 | done = true; |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | // Reset any events that we consumed. |
| 195 | DNBProcessResetEvents(pid, pid_status_event); |
| 196 | |
| 197 | } |
| 198 | } |
| 199 | DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...", __FUNCTION__, arg, pid); |
| 200 | ctx.Events().ResetEvents(event_proc_thread_running); |
| 201 | ctx.Events().SetEvents(event_proc_thread_exiting); |
| 202 | return NULL; |
| 203 | } |
| 204 | |
| 205 | |
| 206 | const char* |
| 207 | RNBContext::EventsAsString (nub_event_t events, std::string& s) |
| 208 | { |
| 209 | s.clear(); |
| 210 | if (events & event_proc_state_changed) |
| 211 | s += "proc_state_changed "; |
| 212 | if (events & event_proc_thread_running) |
| 213 | s += "proc_thread_running "; |
| 214 | if (events & event_proc_thread_exiting) |
| 215 | s += "proc_thread_exiting "; |
| 216 | if (events & event_proc_stdio_available) |
| 217 | s += "proc_stdio_available "; |
| 218 | if (events & event_read_packet_available) |
| 219 | s += "read_packet_available "; |
| 220 | if (events & event_read_thread_running) |
| 221 | s += "read_thread_running "; |
| 222 | if (events & event_read_thread_running) |
| 223 | s += "read_thread_running "; |
| 224 | return s.c_str(); |
| 225 | } |
| 226 | |
| 227 | const char * |
| 228 | RNBContext::LaunchStatusAsString (std::string& s) |
| 229 | { |
| 230 | s.clear(); |
| 231 | |
| 232 | const char *err_str = m_launch_status.AsString(); |
| 233 | if (err_str) |
| 234 | s = err_str; |
| 235 | else |
| 236 | { |
| 237 | char error_num_str[64]; |
| 238 | snprintf(error_num_str, sizeof(error_num_str), "%u", m_launch_status.Error()); |
| 239 | s = error_num_str; |
| 240 | } |
| 241 | return s.c_str(); |
| 242 | } |
| 243 | |
| 244 | bool |
| 245 | RNBContext::ProcessStateRunning() const |
| 246 | { |
| 247 | nub_state_t pid_state = DNBProcessGetState(m_pid); |
| 248 | return pid_state == eStateRunning || pid_state == eStateStepping; |
| 249 | } |