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//----------------------------------------------------------------------
28RNBContext::~RNBContext()
29{
30    SetProcessID (INVALID_NUB_PROCESS);
31}
32
33//----------------------------------------------------------------------
34// RNBContext constructor
35//----------------------------------------------------------------------
36
37const char *
38RNBContext::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
47const char *
48RNBContext::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
56bool
57RNBContext::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
70void
71RNBContext::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
88void
89RNBContext::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
111void
112RNBContext::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//----------------------------------------------------------------------
136void*
137RNBContext::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
215const char*
216RNBContext::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
238const char *
239RNBContext::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
255bool
256RNBContext::ProcessStateRunning() const
257{
258    nub_state_t pid_state = DNBProcessGetState(m_pid);
259    return pid_state == eStateRunning || pid_state == eStateStepping;
260}
261