xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- RNBContext.cpp ------------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 12/12/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "RNBContext.h"
14061da546Spatrick 
15061da546Spatrick #include <sstream>
16061da546Spatrick #include <sys/stat.h>
17061da546Spatrick 
18061da546Spatrick #if defined(__APPLE__)
19061da546Spatrick #include <pthread.h>
20061da546Spatrick #include <sched.h>
21061da546Spatrick #endif
22061da546Spatrick 
23061da546Spatrick #include "CFString.h"
24061da546Spatrick #include "DNB.h"
25061da546Spatrick #include "DNBLog.h"
26061da546Spatrick #include "RNBRemote.h"
27*f6aab3d8Srobert #include "MacOSX/MachException.h"
28061da546Spatrick 
29061da546Spatrick // Destructor
~RNBContext()30061da546Spatrick RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); }
31061da546Spatrick 
32061da546Spatrick // RNBContext constructor
33061da546Spatrick 
EnvironmentAtIndex(size_t index)34061da546Spatrick const char *RNBContext::EnvironmentAtIndex(size_t index) {
35061da546Spatrick   if (index < m_env_vec.size())
36061da546Spatrick     return m_env_vec[index].c_str();
37061da546Spatrick   else
38061da546Spatrick     return NULL;
39061da546Spatrick }
40061da546Spatrick 
GetEnvironmentKey(const std::string & env)41061da546Spatrick static std::string GetEnvironmentKey(const std::string &env) {
42061da546Spatrick   std::string key = env.substr(0, env.find('='));
43061da546Spatrick   if (!key.empty() && key.back() == '=')
44061da546Spatrick     key.pop_back();
45061da546Spatrick   return key;
46061da546Spatrick }
47061da546Spatrick 
PushEnvironmentIfNeeded(const char * arg)48061da546Spatrick void RNBContext::PushEnvironmentIfNeeded(const char *arg) {
49061da546Spatrick   if (!arg)
50061da546Spatrick     return;
51061da546Spatrick   std::string arg_key = GetEnvironmentKey(arg);
52061da546Spatrick 
53061da546Spatrick   for (const std::string &entry: m_env_vec) {
54061da546Spatrick     if (arg_key == GetEnvironmentKey(entry))
55061da546Spatrick       return;
56061da546Spatrick   }
57061da546Spatrick   m_env_vec.push_back(arg);
58061da546Spatrick }
59061da546Spatrick 
ArgumentAtIndex(size_t index)60061da546Spatrick const char *RNBContext::ArgumentAtIndex(size_t index) {
61061da546Spatrick   if (index < m_arg_vec.size())
62061da546Spatrick     return m_arg_vec[index].c_str();
63061da546Spatrick   else
64061da546Spatrick     return NULL;
65061da546Spatrick }
66061da546Spatrick 
SetWorkingDirectory(const char * path)67061da546Spatrick bool RNBContext::SetWorkingDirectory(const char *path) {
68061da546Spatrick   struct stat working_directory_stat;
69061da546Spatrick   if (::stat(path, &working_directory_stat) != 0) {
70061da546Spatrick     m_working_directory.clear();
71061da546Spatrick     return false;
72061da546Spatrick   }
73061da546Spatrick   m_working_directory.assign(path);
74061da546Spatrick   return true;
75061da546Spatrick }
76061da546Spatrick 
SetProcessID(nub_process_t pid)77061da546Spatrick void RNBContext::SetProcessID(nub_process_t pid) {
78061da546Spatrick   // Delete and events we created
79061da546Spatrick   if (m_pid != INVALID_NUB_PROCESS) {
80061da546Spatrick     StopProcessStatusThread();
81061da546Spatrick     // Unregister this context as a client of the process's events.
82061da546Spatrick   }
83061da546Spatrick   // Assign our new process ID
84061da546Spatrick   m_pid = pid;
85061da546Spatrick 
86061da546Spatrick   if (pid != INVALID_NUB_PROCESS) {
87061da546Spatrick     StartProcessStatusThread();
88061da546Spatrick   }
89061da546Spatrick }
90061da546Spatrick 
StartProcessStatusThread()91061da546Spatrick void RNBContext::StartProcessStatusThread() {
92061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
93061da546Spatrick   if ((m_events.GetEventBits() & event_proc_thread_running) == 0) {
94061da546Spatrick     int err = ::pthread_create(&m_pid_pthread, NULL,
95061da546Spatrick                                ThreadFunctionProcessStatus, this);
96061da546Spatrick     if (err == 0) {
97061da546Spatrick       // Our thread was successfully kicked off, wait for it to
98061da546Spatrick       // set the started event so we can safely continue
99061da546Spatrick       m_events.WaitForSetEvents(event_proc_thread_running);
100061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!",
101061da546Spatrick                        __FUNCTION__);
102061da546Spatrick     } else {
103061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PROC,
104061da546Spatrick                        "RNBContext::%s thread failed to start: err = %i",
105061da546Spatrick                        __FUNCTION__, err);
106061da546Spatrick       m_events.ResetEvents(event_proc_thread_running);
107061da546Spatrick       m_events.SetEvents(event_proc_thread_exiting);
108061da546Spatrick     }
109061da546Spatrick   }
110061da546Spatrick }
111061da546Spatrick 
StopProcessStatusThread()112061da546Spatrick void RNBContext::StopProcessStatusThread() {
113061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
114061da546Spatrick   if ((m_events.GetEventBits() & event_proc_thread_running) ==
115061da546Spatrick       event_proc_thread_running) {
116061da546Spatrick     struct timespec timeout_abstime;
117061da546Spatrick     DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
118061da546Spatrick     // Wait for 2 seconds for the rx thread to exit
119061da546Spatrick     if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting,
120061da546Spatrick                                   &timeout_abstime) ==
121061da546Spatrick         RNBContext::event_proc_thread_exiting) {
122061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PROC,
123061da546Spatrick                        "RNBContext::%s thread stopped as requeseted",
124061da546Spatrick                        __FUNCTION__);
125061da546Spatrick     } else {
126061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PROC,
127061da546Spatrick                        "RNBContext::%s thread did not stop in 2 seconds...",
128061da546Spatrick                        __FUNCTION__);
129061da546Spatrick       // Kill the RX thread???
130061da546Spatrick     }
131061da546Spatrick   }
132061da546Spatrick }
133061da546Spatrick 
134061da546Spatrick // This thread's sole purpose is to watch for any status changes in the
135061da546Spatrick // child process.
ThreadFunctionProcessStatus(void * arg)136061da546Spatrick void *RNBContext::ThreadFunctionProcessStatus(void *arg) {
137061da546Spatrick   RNBRemoteSP remoteSP(g_remoteSP);
138061da546Spatrick   RNBRemote *remote = remoteSP.get();
139061da546Spatrick   if (remote == NULL)
140061da546Spatrick     return NULL;
141061da546Spatrick   RNBContext &ctx = remote->Context();
142061da546Spatrick 
143061da546Spatrick   nub_process_t pid = ctx.ProcessID();
144061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PROC,
145061da546Spatrick                    "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...",
146061da546Spatrick                    __FUNCTION__, arg, pid);
147061da546Spatrick   ctx.Events().SetEvents(RNBContext::event_proc_thread_running);
148061da546Spatrick 
149061da546Spatrick #if defined(__APPLE__)
150061da546Spatrick   pthread_setname_np("child process status watcher thread");
151061da546Spatrick #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
152061da546Spatrick   struct sched_param thread_param;
153061da546Spatrick   int thread_sched_policy;
154061da546Spatrick   if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
155061da546Spatrick                             &thread_param) == 0) {
156061da546Spatrick     thread_param.sched_priority = 47;
157061da546Spatrick     pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
158061da546Spatrick   }
159061da546Spatrick #endif
160061da546Spatrick #endif
161061da546Spatrick 
162061da546Spatrick   bool done = false;
163061da546Spatrick   while (!done) {
164061da546Spatrick     DNBLogThreadedIf(LOG_RNB_PROC,
165061da546Spatrick                      "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
166061da546Spatrick                      "eEventProcessRunningStateChanged | "
167061da546Spatrick                      "eEventProcessStoppedStateChanged | eEventStdioAvailable "
168061da546Spatrick                      "| eEventProfileDataAvailable, true)...",
169061da546Spatrick                      __FUNCTION__);
170061da546Spatrick     nub_event_t pid_status_event = DNBProcessWaitForEvents(
171061da546Spatrick         pid,
172061da546Spatrick         eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged |
173061da546Spatrick             eEventStdioAvailable | eEventProfileDataAvailable,
174061da546Spatrick         true, NULL);
175061da546Spatrick     DNBLogThreadedIf(LOG_RNB_PROC,
176061da546Spatrick                      "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
177061da546Spatrick                      "eEventProcessRunningStateChanged | "
178061da546Spatrick                      "eEventProcessStoppedStateChanged | eEventStdioAvailable "
179061da546Spatrick                      "| eEventProfileDataAvailable, true) => 0x%8.8x",
180061da546Spatrick                      __FUNCTION__, pid_status_event);
181061da546Spatrick 
182061da546Spatrick     if (pid_status_event == 0) {
183061da546Spatrick       DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back "
184061da546Spatrick                                      "from DNBProcessWaitForEvent....",
185061da546Spatrick                        __FUNCTION__, pid);
186061da546Spatrick       //    done = true;
187061da546Spatrick     } else {
188061da546Spatrick       if (pid_status_event & eEventStdioAvailable) {
189061da546Spatrick         DNBLogThreadedIf(
190061da546Spatrick             LOG_RNB_PROC,
191061da546Spatrick             "RNBContext::%s (pid=%4.4x) got stdio available event....",
192061da546Spatrick             __FUNCTION__, pid);
193061da546Spatrick         ctx.Events().SetEvents(RNBContext::event_proc_stdio_available);
194061da546Spatrick         // Wait for the main thread to consume this notification if it requested
195061da546Spatrick         // we wait for it
196061da546Spatrick         ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available);
197061da546Spatrick       }
198061da546Spatrick 
199061da546Spatrick       if (pid_status_event & eEventProfileDataAvailable) {
200061da546Spatrick         DNBLogThreadedIf(
201061da546Spatrick             LOG_RNB_PROC,
202061da546Spatrick             "RNBContext::%s (pid=%4.4x) got profile data event....",
203061da546Spatrick             __FUNCTION__, pid);
204061da546Spatrick         ctx.Events().SetEvents(RNBContext::event_proc_profile_data);
205061da546Spatrick         // Wait for the main thread to consume this notification if it requested
206061da546Spatrick         // we wait for it
207061da546Spatrick         ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data);
208061da546Spatrick       }
209061da546Spatrick 
210061da546Spatrick       if (pid_status_event & (eEventProcessRunningStateChanged |
211061da546Spatrick                               eEventProcessStoppedStateChanged)) {
212061da546Spatrick         nub_state_t pid_state = DNBProcessGetState(pid);
213061da546Spatrick         DNBLogThreadedIf(
214061da546Spatrick             LOG_RNB_PROC,
215061da546Spatrick             "RNBContext::%s (pid=%4.4x) got process state change: %s",
216061da546Spatrick             __FUNCTION__, pid, DNBStateAsString(pid_state));
217061da546Spatrick 
218061da546Spatrick         // Let the main thread know there is a process state change to see
219061da546Spatrick         ctx.Events().SetEvents(RNBContext::event_proc_state_changed);
220061da546Spatrick         // Wait for the main thread to consume this notification if it requested
221061da546Spatrick         // we wait for it
222061da546Spatrick         ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed);
223061da546Spatrick 
224061da546Spatrick         switch (pid_state) {
225061da546Spatrick         case eStateStopped:
226061da546Spatrick           break;
227061da546Spatrick 
228061da546Spatrick         case eStateInvalid:
229061da546Spatrick         case eStateExited:
230061da546Spatrick         case eStateDetached:
231061da546Spatrick           done = true;
232061da546Spatrick           break;
233061da546Spatrick         default:
234061da546Spatrick           break;
235061da546Spatrick         }
236061da546Spatrick       }
237061da546Spatrick 
238061da546Spatrick       // Reset any events that we consumed.
239061da546Spatrick       DNBProcessResetEvents(pid, pid_status_event);
240061da546Spatrick     }
241061da546Spatrick   }
242061da546Spatrick   DNBLogThreadedIf(LOG_RNB_PROC,
243061da546Spatrick                    "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...",
244061da546Spatrick                    __FUNCTION__, arg, pid);
245061da546Spatrick   ctx.Events().ResetEvents(event_proc_thread_running);
246061da546Spatrick   ctx.Events().SetEvents(event_proc_thread_exiting);
247061da546Spatrick   return NULL;
248061da546Spatrick }
249061da546Spatrick 
EventsAsString(nub_event_t events,std::string & s)250061da546Spatrick const char *RNBContext::EventsAsString(nub_event_t events, std::string &s) {
251061da546Spatrick   s.clear();
252061da546Spatrick   if (events & event_proc_state_changed)
253061da546Spatrick     s += "proc_state_changed ";
254061da546Spatrick   if (events & event_proc_thread_running)
255061da546Spatrick     s += "proc_thread_running ";
256061da546Spatrick   if (events & event_proc_thread_exiting)
257061da546Spatrick     s += "proc_thread_exiting ";
258061da546Spatrick   if (events & event_proc_stdio_available)
259061da546Spatrick     s += "proc_stdio_available ";
260061da546Spatrick   if (events & event_proc_profile_data)
261061da546Spatrick     s += "proc_profile_data ";
262061da546Spatrick   if (events & event_read_packet_available)
263061da546Spatrick     s += "read_packet_available ";
264061da546Spatrick   if (events & event_read_thread_running)
265061da546Spatrick     s += "read_thread_running ";
266061da546Spatrick   if (events & event_read_thread_running)
267061da546Spatrick     s += "read_thread_running ";
268061da546Spatrick   return s.c_str();
269061da546Spatrick }
270061da546Spatrick 
LaunchStatusAsString(std::string & s)271061da546Spatrick const char *RNBContext::LaunchStatusAsString(std::string &s) {
272061da546Spatrick   s.clear();
273061da546Spatrick 
274061da546Spatrick   const char *err_str = m_launch_status.AsString();
275061da546Spatrick   if (err_str)
276061da546Spatrick     s = err_str;
277061da546Spatrick   else {
278061da546Spatrick     char error_num_str[64];
279061da546Spatrick     snprintf(error_num_str, sizeof(error_num_str), "%u",
280061da546Spatrick              m_launch_status.Status());
281061da546Spatrick     s = error_num_str;
282061da546Spatrick   }
283061da546Spatrick   return s.c_str();
284061da546Spatrick }
285061da546Spatrick 
ProcessStateRunning() const286061da546Spatrick bool RNBContext::ProcessStateRunning() const {
287061da546Spatrick   nub_state_t pid_state = DNBProcessGetState(m_pid);
288061da546Spatrick   return pid_state == eStateRunning || pid_state == eStateStepping;
289061da546Spatrick }
290*f6aab3d8Srobert 
AddIgnoredException(const char * exception_name)291*f6aab3d8Srobert bool RNBContext::AddIgnoredException(const char *exception_name) {
292*f6aab3d8Srobert   exception_mask_t exc_mask = MachException::ExceptionMask(exception_name);
293*f6aab3d8Srobert   if (exc_mask == 0)
294*f6aab3d8Srobert     return false;
295*f6aab3d8Srobert   m_ignored_exceptions.push_back(exc_mask);
296*f6aab3d8Srobert   return true;
297*f6aab3d8Srobert }
298*f6aab3d8Srobert 
AddDefaultIgnoredExceptions()299*f6aab3d8Srobert void RNBContext::AddDefaultIgnoredExceptions() {
300*f6aab3d8Srobert   m_ignored_exceptions.push_back(EXC_MASK_BAD_ACCESS);
301*f6aab3d8Srobert   m_ignored_exceptions.push_back(EXC_MASK_BAD_INSTRUCTION);
302*f6aab3d8Srobert   m_ignored_exceptions.push_back(EXC_MASK_ARITHMETIC);
303*f6aab3d8Srobert }
304