1 //===-- DNB.cpp -------------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  Created by Greg Clayton on 3/23/07.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "DNB.h"
14 #include <cinttypes>
15 #include <csignal>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <libproc.h>
19 #include <map>
20 #include <sys/resource.h>
21 #include <sys/stat.h>
22 #include <sys/sysctl.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 #include <vector>
27 
28 #if defined(__APPLE__)
29 #include <pthread.h>
30 #include <sched.h>
31 #endif
32 
33 #define TRY_KQUEUE 1
34 
35 #ifdef TRY_KQUEUE
36 #include <sys/event.h>
37 #include <sys/time.h>
38 #ifdef NOTE_EXIT_DETAIL
39 #define USE_KQUEUE
40 #endif
41 #endif
42 
43 #include "CFBundle.h"
44 #include "CFString.h"
45 #include "DNBDataRef.h"
46 #include "DNBLog.h"
47 #include "DNBThreadResumeActions.h"
48 #include "DNBTimer.h"
49 #include "MacOSX/Genealogy.h"
50 #include "MacOSX/MachProcess.h"
51 #include "MacOSX/MachTask.h"
52 #include "MacOSX/ThreadInfo.h"
53 
54 typedef std::shared_ptr<MachProcess> MachProcessSP;
55 typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
56 typedef ProcessMap::iterator ProcessMapIter;
57 typedef ProcessMap::const_iterator ProcessMapConstIter;
58 
59 static size_t
60 GetAllInfosMatchingName(const char *process_name,
61                         std::vector<struct kinfo_proc> &matching_proc_infos);
62 
63 // A Thread safe singleton to get a process map pointer.
64 //
65 // Returns a pointer to the existing process map, or a pointer to a
66 // newly created process map if CAN_CREATE is non-zero.
GetProcessMap(bool can_create)67 static ProcessMap *GetProcessMap(bool can_create) {
68   static ProcessMap *g_process_map_ptr = NULL;
69 
70   if (can_create && g_process_map_ptr == NULL) {
71     static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
72     PTHREAD_MUTEX_LOCKER(locker, &g_process_map_mutex);
73     if (g_process_map_ptr == NULL)
74       g_process_map_ptr = new ProcessMap;
75   }
76   return g_process_map_ptr;
77 }
78 
79 // Add PID to the shared process pointer map.
80 //
81 // Return non-zero value if we succeed in adding the process to the map.
82 // The only time this should fail is if we run out of memory and can't
83 // allocate a ProcessMap.
AddProcessToMap(nub_process_t pid,MachProcessSP & procSP)84 static nub_bool_t AddProcessToMap(nub_process_t pid, MachProcessSP &procSP) {
85   ProcessMap *process_map = GetProcessMap(true);
86   if (process_map) {
87     process_map->insert(std::make_pair(pid, procSP));
88     return true;
89   }
90   return false;
91 }
92 
93 // Remove the shared pointer for PID from the process map.
94 //
95 // Returns the number of items removed from the process map.
96 // static size_t
97 // RemoveProcessFromMap (nub_process_t pid)
98 //{
99 //    ProcessMap* process_map = GetProcessMap(false);
100 //    if (process_map)
101 //    {
102 //        return process_map->erase(pid);
103 //    }
104 //    return 0;
105 //}
106 
107 // Get the shared pointer for PID from the existing process map.
108 //
109 // Returns true if we successfully find a shared pointer to a
110 // MachProcess object.
GetProcessSP(nub_process_t pid,MachProcessSP & procSP)111 static nub_bool_t GetProcessSP(nub_process_t pid, MachProcessSP &procSP) {
112   ProcessMap *process_map = GetProcessMap(false);
113   if (process_map != NULL) {
114     ProcessMapIter pos = process_map->find(pid);
115     if (pos != process_map->end()) {
116       procSP = pos->second;
117       return true;
118     }
119   }
120   procSP.reset();
121   return false;
122 }
123 
124 #ifdef USE_KQUEUE
kqueue_thread(void * arg)125 void *kqueue_thread(void *arg) {
126   int kq_id = (int)(intptr_t)arg;
127 
128 #if defined(__APPLE__)
129   pthread_setname_np("kqueue thread");
130 #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
131   struct sched_param thread_param;
132   int thread_sched_policy;
133   if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
134                             &thread_param) == 0) {
135     thread_param.sched_priority = 47;
136     pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
137   }
138 #endif
139 #endif
140 
141   struct kevent death_event;
142   while (true) {
143     int n_events = kevent(kq_id, NULL, 0, &death_event, 1, NULL);
144     if (n_events == -1) {
145       if (errno == EINTR)
146         continue;
147       else {
148         DNBLogError("kqueue failed with error: (%d): %s", errno,
149                     strerror(errno));
150         return NULL;
151       }
152     } else if (death_event.flags & EV_ERROR) {
153       int error_no = static_cast<int>(death_event.data);
154       const char *error_str = strerror(error_no);
155       if (error_str == NULL)
156         error_str = "Unknown error";
157       DNBLogError("Failed to initialize kqueue event: (%d): %s", error_no,
158                   error_str);
159       return NULL;
160     } else {
161       int status;
162       const pid_t pid = (pid_t)death_event.ident;
163       const pid_t child_pid = waitpid(pid, &status, 0);
164 
165       bool exited = false;
166       int signal = 0;
167       int exit_status = 0;
168       if (WIFSTOPPED(status)) {
169         signal = WSTOPSIG(status);
170         DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)",
171                          child_pid, signal);
172       } else if (WIFEXITED(status)) {
173         exit_status = WEXITSTATUS(status);
174         exited = true;
175         DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)",
176                          child_pid, exit_status);
177       } else if (WIFSIGNALED(status)) {
178         signal = WTERMSIG(status);
179         if (child_pid == abs(pid)) {
180           DNBLogThreadedIf(LOG_PROCESS,
181                            "waitpid (%i) -> SIGNALED and EXITED (signal = %i)",
182                            child_pid, signal);
183           char exit_info[64];
184           ::snprintf(exit_info, sizeof(exit_info),
185                      "Terminated due to signal %i", signal);
186           DNBProcessSetExitInfo(child_pid, exit_info);
187           exited = true;
188           exit_status = INT8_MAX;
189         } else {
190           DNBLogThreadedIf(LOG_PROCESS,
191                            "waitpid (%i) -> SIGNALED (signal = %i)", child_pid,
192                            signal);
193         }
194       }
195 
196       if (exited) {
197         if (death_event.data & NOTE_EXIT_MEMORY)
198           DNBProcessSetExitInfo(child_pid, "Terminated due to memory issue");
199         else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)
200           DNBProcessSetExitInfo(child_pid, "Terminated due to decrypt failure");
201         else if (death_event.data & NOTE_EXIT_CSERROR)
202           DNBProcessSetExitInfo(child_pid,
203                                 "Terminated due to code signing error");
204 
205         DNBLogThreadedIf(
206             LOG_PROCESS,
207             "waitpid_process_thread (): setting exit status for pid = %i to %i",
208             child_pid, exit_status);
209         DNBProcessSetExitStatus(child_pid, status);
210         return NULL;
211       }
212     }
213   }
214 }
215 
spawn_kqueue_thread(pid_t pid)216 static bool spawn_kqueue_thread(pid_t pid) {
217   pthread_t thread;
218   int kq_id;
219 
220   kq_id = kqueue();
221   if (kq_id == -1) {
222     DNBLogError("Could not get kqueue for pid = %i.", pid);
223     return false;
224   }
225 
226   struct kevent reg_event;
227 
228   EV_SET(&reg_event, pid, EVFILT_PROC, EV_ADD,
229          NOTE_EXIT | NOTE_EXITSTATUS | NOTE_EXIT_DETAIL, 0, NULL);
230   // Register the event:
231   int result = kevent(kq_id, &reg_event, 1, NULL, 0, NULL);
232   if (result != 0) {
233     DNBLogError(
234         "Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid,
235         result);
236     return false;
237   }
238 
239   int ret =
240       ::pthread_create(&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);
241 
242   // pthread_create returns 0 if successful
243   if (ret == 0) {
244     ::pthread_detach(thread);
245     return true;
246   }
247   return false;
248 }
249 #endif // #if USE_KQUEUE
250 
waitpid_thread(void * arg)251 static void *waitpid_thread(void *arg) {
252   const pid_t pid = (pid_t)(intptr_t)arg;
253   int status;
254 
255 #if defined(__APPLE__)
256   pthread_setname_np("waitpid thread");
257 #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
258   struct sched_param thread_param;
259   int thread_sched_policy;
260   if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
261                             &thread_param) == 0) {
262     thread_param.sched_priority = 47;
263     pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
264   }
265 #endif
266 #endif
267 
268   while (true) {
269     pid_t child_pid = waitpid(pid, &status, 0);
270     DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, "
271                                   "&status, 0) => %i, status = %i, errno = %i",
272                      pid, child_pid, status, errno);
273 
274     if (child_pid < 0) {
275       if (errno == EINTR)
276         continue;
277       break;
278     } else {
279       if (WIFSTOPPED(status)) {
280         continue;
281       } else // if (WIFEXITED(status) || WIFSIGNALED(status))
282       {
283         DNBLogThreadedIf(
284             LOG_PROCESS,
285             "waitpid_thread (): setting exit status for pid = %i to %i",
286             child_pid, status);
287         DNBProcessSetExitStatus(child_pid, status);
288         return NULL;
289       }
290     }
291   }
292 
293   // We should never exit as long as our child process is alive, so if we
294   // do something else went wrong and we should exit...
295   DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting "
296                                 "exit status to an invalid value (-1) for pid "
297                                 "%i",
298                    pid);
299   DNBProcessSetExitStatus(pid, -1);
300   return NULL;
301 }
spawn_waitpid_thread(pid_t pid)302 static bool spawn_waitpid_thread(pid_t pid) {
303 #ifdef USE_KQUEUE
304   bool success = spawn_kqueue_thread(pid);
305   if (success)
306     return true;
307 #endif
308 
309   pthread_t thread;
310   int ret =
311       ::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
312   // pthread_create returns 0 if successful
313   if (ret == 0) {
314     ::pthread_detach(thread);
315     return true;
316   }
317   return false;
318 }
319 
DNBProcessLaunch(RNBContext * ctx,const char * path,char const * argv[],const char * envp[],const char * working_directory,const char * stdin_path,const char * stdout_path,const char * stderr_path,bool no_stdio,int disable_aslr,const char * event_data,char * err_str,size_t err_len)320 nub_process_t DNBProcessLaunch(
321     RNBContext *ctx, const char *path, char const *argv[], const char *envp[],
322     const char *working_directory, // NULL => don't change, non-NULL => set
323                                    // working directory for inferior to this
324     const char *stdin_path, const char *stdout_path, const char *stderr_path,
325     bool no_stdio, int disable_aslr, const char *event_data, char *err_str,
326     size_t err_len) {
327   DNBLogThreadedIf(LOG_PROCESS,
328                    "%s ( path='%s', argv = %p, envp = %p, "
329                    "working_dir=%s, stdin=%s, stdout=%s, "
330                    "stderr=%s, no-stdio=%i, launch_flavor = %u, "
331                    "disable_aslr = %d, err = %p, err_len = "
332                    "%llu) called...",
333                    __FUNCTION__, path, static_cast<void *>(argv),
334                    static_cast<void *>(envp), working_directory, stdin_path,
335                    stdout_path, stderr_path, no_stdio, ctx->LaunchFlavor(),
336                    disable_aslr, static_cast<void *>(err_str),
337                    static_cast<uint64_t>(err_len));
338 
339   if (err_str && err_len > 0)
340     err_str[0] = '\0';
341   struct stat path_stat;
342   if (::stat(path, &path_stat) == -1) {
343     char stat_error[256];
344     ::strerror_r(errno, stat_error, sizeof(stat_error));
345     snprintf(err_str, err_len, "%s (%s)", stat_error, path);
346     return INVALID_NUB_PROCESS;
347   }
348 
349   MachProcessSP processSP(new MachProcess);
350   if (processSP.get()) {
351     DNBError launch_err;
352     pid_t pid = processSP->LaunchForDebug(
353         path, argv, envp, working_directory, stdin_path, stdout_path,
354         stderr_path, no_stdio, ctx->LaunchFlavor(), disable_aslr, event_data,
355         ctx->GetIgnoredExceptions(), launch_err);
356     if (err_str) {
357       *err_str = '\0';
358       if (launch_err.Fail()) {
359         const char *launch_err_str = launch_err.AsString();
360         if (launch_err_str) {
361           strlcpy(err_str, launch_err_str, err_len - 1);
362           err_str[err_len - 1] =
363               '\0'; // Make sure the error string is terminated
364         }
365       }
366     }
367 
368     DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
369 
370     if (pid != INVALID_NUB_PROCESS) {
371       // Spawn a thread to reap our child inferior process...
372       spawn_waitpid_thread(pid);
373 
374       if (processSP->Task().TaskPortForProcessID(launch_err) == TASK_NULL) {
375         // We failed to get the task for our process ID which is bad.
376         // Kill our process otherwise it will be stopped at the entry
377         // point and get reparented to someone else and never go away.
378         DNBLog("Could not get task port for process, sending SIGKILL and "
379                "exiting.");
380         kill(SIGKILL, pid);
381 
382         if (err_str && err_len > 0) {
383           if (launch_err.AsString()) {
384             ::snprintf(err_str, err_len,
385                        "failed to get the task for process %i (%s)", pid,
386                        launch_err.AsString());
387           } else {
388             ::snprintf(err_str, err_len,
389                        "failed to get the task for process %i", pid);
390           }
391         }
392       } else {
393         bool res = AddProcessToMap(pid, processSP);
394         UNUSED_IF_ASSERT_DISABLED(res);
395         assert(res && "Couldn't add process to map!");
396         return pid;
397       }
398     }
399   }
400   return INVALID_NUB_PROCESS;
401 }
402 
403 // If there is one process with a given name, return the pid for that process.
DNBProcessGetPIDByName(const char * name)404 nub_process_t DNBProcessGetPIDByName(const char *name) {
405   std::vector<struct kinfo_proc> matching_proc_infos;
406   size_t num_matching_proc_infos =
407       GetAllInfosMatchingName(name, matching_proc_infos);
408   if (num_matching_proc_infos == 1) {
409     return matching_proc_infos[0].kp_proc.p_pid;
410   }
411   return INVALID_NUB_PROCESS;
412 }
413 
DNBProcessAttachByName(const char * name,struct timespec * timeout,const RNBContext::IgnoredExceptions & ignored_exceptions,char * err_str,size_t err_len)414 nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
415                                      const RNBContext::IgnoredExceptions
416                                              &ignored_exceptions, char *err_str,
417                                      size_t err_len) {
418   if (err_str && err_len > 0)
419     err_str[0] = '\0';
420   std::vector<struct kinfo_proc> matching_proc_infos;
421   size_t num_matching_proc_infos =
422       GetAllInfosMatchingName(name, matching_proc_infos);
423   if (num_matching_proc_infos == 0) {
424     DNBLogError("error: no processes match '%s'\n", name);
425     return INVALID_NUB_PROCESS;
426   }
427   if (num_matching_proc_infos > 1) {
428     DNBLogError("error: %llu processes match '%s':\n",
429                 (uint64_t)num_matching_proc_infos, name);
430     size_t i;
431     for (i = 0; i < num_matching_proc_infos; ++i)
432       DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,
433                   matching_proc_infos[i].kp_proc.p_comm);
434     return INVALID_NUB_PROCESS;
435   }
436 
437   return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,
438                           ignored_exceptions, err_str, err_len);
439 }
440 
DNBProcessAttach(nub_process_t attach_pid,struct timespec * timeout,const RNBContext::IgnoredExceptions & ignored_exceptions,char * err_str,size_t err_len)441 nub_process_t DNBProcessAttach(nub_process_t attach_pid,
442                                struct timespec *timeout,
443                                const RNBContext::IgnoredExceptions
444                                        &ignored_exceptions,
445                                char *err_str, size_t err_len) {
446   if (err_str && err_len > 0)
447     err_str[0] = '\0';
448 
449   if (getenv("LLDB_DEBUGSERVER_PATH") == NULL) {
450     int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,
451                  static_cast<int>(attach_pid)};
452     struct kinfo_proc processInfo;
453     size_t bufsize = sizeof(processInfo);
454     if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo,
455                &bufsize, NULL, 0) == 0 &&
456         bufsize > 0) {
457 
458       if ((processInfo.kp_proc.p_flag & P_TRANSLATED) == P_TRANSLATED) {
459         const char *translated_debugserver =
460             "/Library/Apple/usr/libexec/oah/debugserver";
461         char fdstr[16];
462         char pidstr[16];
463         extern int communication_fd;
464 
465         if (communication_fd == -1) {
466           fprintf(stderr, "Trying to attach to a translated process with the "
467                           "native debugserver, exiting...\n");
468           exit(1);
469         }
470 
471         snprintf(fdstr, sizeof(fdstr), "--fd=%d", communication_fd);
472         snprintf(pidstr, sizeof(pidstr), "--attach=%d", attach_pid);
473         execl(translated_debugserver, translated_debugserver, "--native-regs",
474               "--setsid", fdstr, "--handoff-attach-from-native", pidstr,
475               (char *)0);
476         DNBLogThreadedIf(LOG_PROCESS, "Failed to launch debugserver for "
477                          "translated process: ", errno, strerror(errno));
478         __builtin_trap();
479       }
480     }
481   }
482 
483   if (DNBDebugserverIsTranslated()) {
484     return INVALID_NUB_PROCESS_ARCH;
485   }
486 
487   pid_t pid = INVALID_NUB_PROCESS;
488   MachProcessSP processSP(new MachProcess);
489   if (processSP.get()) {
490     DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",
491                      attach_pid);
492     pid =
493         processSP->AttachForDebug(attach_pid, ignored_exceptions, err_str,
494                                   err_len);
495 
496     if (pid != INVALID_NUB_PROCESS) {
497       bool res = AddProcessToMap(pid, processSP);
498       UNUSED_IF_ASSERT_DISABLED(res);
499       assert(res && "Couldn't add process to map!");
500       spawn_waitpid_thread(pid);
501     }
502   }
503 
504   while (pid != INVALID_NUB_PROCESS) {
505     // Wait for process to start up and hit entry point
506     DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
507                                   "eEventProcessRunningStateChanged | "
508                                   "eEventProcessStoppedStateChanged, true, "
509                                   "INFINITE)...",
510                      __FUNCTION__, pid);
511     nub_event_t set_events =
512         DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
513                                          eEventProcessStoppedStateChanged,
514                                 true, timeout);
515 
516     DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
517                                   "eEventProcessRunningStateChanged | "
518                                   "eEventProcessStoppedStateChanged, true, "
519                                   "INFINITE) => 0x%8.8x",
520                      __FUNCTION__, pid, set_events);
521 
522     if (set_events == 0) {
523       if (err_str && err_len > 0)
524         snprintf(err_str, err_len, "operation timed out");
525       pid = INVALID_NUB_PROCESS;
526     } else {
527       if (set_events & (eEventProcessRunningStateChanged |
528                         eEventProcessStoppedStateChanged)) {
529         nub_state_t pid_state = DNBProcessGetState(pid);
530         DNBLogThreadedIf(
531             LOG_PROCESS,
532             "%s process %4.4x state changed (eEventProcessStateChanged): %s",
533             __FUNCTION__, pid, DNBStateAsString(pid_state));
534 
535         switch (pid_state) {
536         case eStateInvalid:
537         case eStateUnloaded:
538         case eStateAttaching:
539         case eStateLaunching:
540         case eStateSuspended:
541           break; // Ignore
542 
543         case eStateRunning:
544         case eStateStepping:
545           // Still waiting to stop at entry point...
546           break;
547 
548         case eStateStopped:
549         case eStateCrashed:
550           return pid;
551 
552         case eStateDetached:
553         case eStateExited:
554           if (err_str && err_len > 0)
555             snprintf(err_str, err_len, "process exited");
556           return INVALID_NUB_PROCESS;
557         }
558       }
559 
560       DNBProcessResetEvents(pid, set_events);
561     }
562   }
563 
564   return INVALID_NUB_PROCESS;
565 }
566 
DNBGetAllInfos(std::vector<struct kinfo_proc> & proc_infos)567 size_t DNBGetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
568   size_t size = 0;
569   int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
570   u_int namelen = sizeof(name) / sizeof(int);
571   int err;
572 
573   // Try to find out how many processes are around so we can
574   // size the buffer appropriately.  sysctl's man page specifically suggests
575   // this approach, and says it returns a bit larger size than needed to
576   // handle any new processes created between then and now.
577 
578   err = ::sysctl(name, namelen, NULL, &size, NULL, 0);
579 
580   if ((err < 0) && (err != ENOMEM)) {
581     proc_infos.clear();
582     perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
583     return 0;
584   }
585 
586   // Increase the size of the buffer by a few processes in case more have
587   // been spawned
588   proc_infos.resize(size / sizeof(struct kinfo_proc));
589   size = proc_infos.size() *
590          sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
591   err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);
592   if (err < 0) {
593     proc_infos.clear();
594     return 0;
595   }
596 
597   // Trim down our array to fit what we actually got back
598   proc_infos.resize(size / sizeof(struct kinfo_proc));
599   return proc_infos.size();
600 }
601 
DNBGetDyldProcessState(nub_process_t pid)602 JSONGenerator::ObjectSP DNBGetDyldProcessState(nub_process_t pid) {
603   MachProcessSP procSP;
604   if (GetProcessSP(pid, procSP)) {
605     return procSP->GetDyldProcessState();
606   }
607   return {};
608 }
609 
610 static size_t
GetAllInfosMatchingName(const char * full_process_name,std::vector<struct kinfo_proc> & matching_proc_infos)611 GetAllInfosMatchingName(const char *full_process_name,
612                         std::vector<struct kinfo_proc> &matching_proc_infos) {
613 
614   matching_proc_infos.clear();
615   if (full_process_name && full_process_name[0]) {
616     // We only get the process name, not the full path, from the proc_info.  So
617     // just take the
618     // base name of the process name...
619     const char *process_name;
620     process_name = strrchr(full_process_name, '/');
621     if (process_name == NULL)
622       process_name = full_process_name;
623     else
624       process_name++;
625 
626     const size_t process_name_len = strlen(process_name);
627     std::vector<struct kinfo_proc> proc_infos;
628     const size_t num_proc_infos = DNBGetAllInfos(proc_infos);
629     if (num_proc_infos > 0) {
630       uint32_t i;
631       for (i = 0; i < num_proc_infos; i++) {
632         // Skip zombie processes and processes with unset status
633         if (proc_infos[i].kp_proc.p_stat == 0 ||
634             proc_infos[i].kp_proc.p_stat == SZOMB)
635           continue;
636 
637         // Check for process by name. We only check the first MAXCOMLEN
638         // chars as that is all that kp_proc.p_comm holds.
639 
640         if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,
641                           MAXCOMLEN) == 0) {
642           if (process_name_len > MAXCOMLEN) {
643             // We found a matching process name whose first MAXCOMLEN
644             // characters match, but there is more to the name than
645             // this. We need to get the full process name.  Use proc_pidpath,
646             // which will get
647             // us the full path to the executed process.
648 
649             char proc_path_buf[PATH_MAX];
650 
651             int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,
652                                           proc_path_buf, PATH_MAX);
653             if (return_val > 0) {
654               // Okay, now search backwards from that to see if there is a
655               // slash in the name.  Note, even though we got all the args we
656               // don't care
657               // because the list data is just a bunch of concatenated null
658               // terminated strings
659               // so strrchr will start from the end of argv0.
660 
661               const char *argv_basename = strrchr(proc_path_buf, '/');
662               if (argv_basename) {
663                 // Skip the '/'
664                 ++argv_basename;
665               } else {
666                 // We didn't find a directory delimiter in the process argv[0],
667                 // just use what was in there
668                 argv_basename = proc_path_buf;
669               }
670 
671               if (argv_basename) {
672                 if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {
673                   matching_proc_infos.push_back(proc_infos[i]);
674                 }
675               }
676             }
677           } else {
678             // We found a matching process, add it to our list
679             matching_proc_infos.push_back(proc_infos[i]);
680           }
681         }
682       }
683     }
684   }
685   // return the newly added matches.
686   return matching_proc_infos.size();
687 }
688 
689 nub_process_t
DNBProcessAttachWait(RNBContext * ctx,const char * waitfor_process_name,bool ignore_existing,struct timespec * timeout_abstime,useconds_t waitfor_interval,char * err_str,size_t err_len,DNBShouldCancelCallback should_cancel_callback,void * callback_data)690 DNBProcessAttachWait(RNBContext *ctx, const char *waitfor_process_name,
691                      bool ignore_existing, struct timespec *timeout_abstime,
692                      useconds_t waitfor_interval, char *err_str, size_t err_len,
693                      DNBShouldCancelCallback should_cancel_callback,
694                      void *callback_data) {
695   DNBError prepare_error;
696   std::vector<struct kinfo_proc> exclude_proc_infos;
697   size_t num_exclude_proc_infos;
698 
699   nub_launch_flavor_t launch_flavor = ctx->LaunchFlavor();
700 
701   // If the PrepareForAttach returns a valid token, use  MachProcess to check
702   // for the process, otherwise scan the process table.
703 
704   const void *attach_token = MachProcess::PrepareForAttach(
705       waitfor_process_name, launch_flavor, true, prepare_error);
706 
707   if (prepare_error.Fail()) {
708     DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());
709     return INVALID_NUB_PROCESS;
710   }
711 
712   if (attach_token == NULL) {
713     if (ignore_existing)
714       num_exclude_proc_infos =
715           GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);
716     else
717       num_exclude_proc_infos = 0;
718   }
719 
720   DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",
721                    waitfor_process_name);
722 
723   // Loop and try to find the process by name
724   nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
725 
726   while (waitfor_pid == INVALID_NUB_PROCESS) {
727     if (attach_token != NULL) {
728       nub_process_t pid;
729       pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
730       if (pid != INVALID_NUB_PROCESS) {
731         waitfor_pid = pid;
732         break;
733       }
734     } else {
735 
736       // Get the current process list, and check for matches that
737       // aren't in our original list. If anyone wants to attach
738       // to an existing process by name, they should do it with
739       // --attach=PROCNAME. Else we will wait for the first matching
740       // process that wasn't in our exclusion list.
741       std::vector<struct kinfo_proc> proc_infos;
742       const size_t num_proc_infos =
743           GetAllInfosMatchingName(waitfor_process_name, proc_infos);
744       for (size_t i = 0; i < num_proc_infos; i++) {
745         nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
746         for (size_t j = 0; j < num_exclude_proc_infos; j++) {
747           if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {
748             // This process was in our exclusion list, don't use it.
749             curr_pid = INVALID_NUB_PROCESS;
750             break;
751           }
752         }
753 
754         // If we didn't find CURR_PID in our exclusion list, then use it.
755         if (curr_pid != INVALID_NUB_PROCESS) {
756           // We found our process!
757           waitfor_pid = curr_pid;
758           break;
759         }
760       }
761     }
762 
763     // If we haven't found our process yet, check for a timeout
764     // and then sleep for a bit until we poll again.
765     if (waitfor_pid == INVALID_NUB_PROCESS) {
766       if (timeout_abstime != NULL) {
767         // Check to see if we have a waitfor-duration option that
768         // has timed out?
769         if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {
770           if (err_str && err_len > 0)
771             snprintf(err_str, err_len, "operation timed out");
772           DNBLogError("error: waiting for process '%s' timed out.\n",
773                       waitfor_process_name);
774           return INVALID_NUB_PROCESS;
775         }
776       }
777 
778       // Call the should cancel callback as well...
779 
780       if (should_cancel_callback != NULL &&
781           should_cancel_callback(callback_data)) {
782         DNBLogThreadedIf(
783             LOG_PROCESS,
784             "DNBProcessAttachWait cancelled by should_cancel callback.");
785         waitfor_pid = INVALID_NUB_PROCESS;
786         break;
787       }
788 
789       ::usleep(waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
790     }
791   }
792 
793   if (waitfor_pid != INVALID_NUB_PROCESS) {
794     DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",
795                      waitfor_process_name, waitfor_pid);
796     waitfor_pid = DNBProcessAttach(waitfor_pid, timeout_abstime,
797                                    ctx->GetIgnoredExceptions(), err_str,
798                                    err_len);
799   }
800 
801   bool success = waitfor_pid != INVALID_NUB_PROCESS;
802   MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,
803                                   prepare_error);
804 
805   return waitfor_pid;
806 }
807 
DNBProcessDetach(nub_process_t pid)808 nub_bool_t DNBProcessDetach(nub_process_t pid) {
809   MachProcessSP procSP;
810   if (GetProcessSP(pid, procSP)) {
811     const bool remove = true;
812     DNBLogThreaded(
813         "Disabling breakpoints and watchpoints, and detaching from %d.", pid);
814     procSP->DisableAllBreakpoints(remove);
815     procSP->DisableAllWatchpoints(remove);
816     return procSP->Detach();
817   }
818   return false;
819 }
820 
DNBProcessKill(nub_process_t pid)821 nub_bool_t DNBProcessKill(nub_process_t pid) {
822   MachProcessSP procSP;
823   if (GetProcessSP(pid, procSP)) {
824     return procSP->Kill();
825   }
826   return false;
827 }
828 
DNBProcessSignal(nub_process_t pid,int signal)829 nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {
830   MachProcessSP procSP;
831   if (GetProcessSP(pid, procSP)) {
832     return procSP->Signal(signal);
833   }
834   return false;
835 }
836 
DNBProcessInterrupt(nub_process_t pid)837 nub_bool_t DNBProcessInterrupt(nub_process_t pid) {
838   MachProcessSP procSP;
839   if (GetProcessSP(pid, procSP))
840     return procSP->Interrupt();
841   return false;
842 }
843 
DNBProcessSendEvent(nub_process_t pid,const char * event)844 nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {
845   MachProcessSP procSP;
846   if (GetProcessSP(pid, procSP)) {
847     // FIXME: Do something with the error...
848     DNBError send_error;
849     return procSP->SendEvent(event, send_error);
850   }
851   return false;
852 }
853 
DNBProcessIsAlive(nub_process_t pid)854 nub_bool_t DNBProcessIsAlive(nub_process_t pid) {
855   MachProcessSP procSP;
856   if (GetProcessSP(pid, procSP)) {
857     return MachTask::IsValid(procSP->Task().TaskPort());
858   }
859   return eStateInvalid;
860 }
861 
862 // Process and Thread state information
DNBProcessGetState(nub_process_t pid)863 nub_state_t DNBProcessGetState(nub_process_t pid) {
864   MachProcessSP procSP;
865   if (GetProcessSP(pid, procSP)) {
866     return procSP->GetState();
867   }
868   return eStateInvalid;
869 }
870 
871 // Process and Thread state information
DNBProcessGetExitStatus(nub_process_t pid,int * status)872 nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {
873   MachProcessSP procSP;
874   if (GetProcessSP(pid, procSP)) {
875     return procSP->GetExitStatus(status);
876   }
877   return false;
878 }
879 
DNBProcessSetExitStatus(nub_process_t pid,int status)880 nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {
881   MachProcessSP procSP;
882   if (GetProcessSP(pid, procSP)) {
883     procSP->SetExitStatus(status);
884     return true;
885   }
886   return false;
887 }
888 
DNBProcessGetExitInfo(nub_process_t pid)889 const char *DNBProcessGetExitInfo(nub_process_t pid) {
890   MachProcessSP procSP;
891   if (GetProcessSP(pid, procSP)) {
892     return procSP->GetExitInfo();
893   }
894   return NULL;
895 }
896 
DNBProcessSetExitInfo(nub_process_t pid,const char * info)897 nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {
898   MachProcessSP procSP;
899   if (GetProcessSP(pid, procSP)) {
900     procSP->SetExitInfo(info);
901     return true;
902   }
903   return false;
904 }
905 
DNBThreadGetName(nub_process_t pid,nub_thread_t tid)906 const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {
907   MachProcessSP procSP;
908   if (GetProcessSP(pid, procSP))
909     return procSP->ThreadGetName(tid);
910   return NULL;
911 }
912 
913 nub_bool_t
DNBThreadGetIdentifierInfo(nub_process_t pid,nub_thread_t tid,thread_identifier_info_data_t * ident_info)914 DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
915                            thread_identifier_info_data_t *ident_info) {
916   MachProcessSP procSP;
917   if (GetProcessSP(pid, procSP))
918     return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
919   return false;
920 }
921 
DNBThreadGetState(nub_process_t pid,nub_thread_t tid)922 nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {
923   MachProcessSP procSP;
924   if (GetProcessSP(pid, procSP)) {
925     return procSP->ThreadGetState(tid);
926   }
927   return eStateInvalid;
928 }
929 
DNBStateAsString(nub_state_t state)930 const char *DNBStateAsString(nub_state_t state) {
931   switch (state) {
932   case eStateInvalid:
933     return "Invalid";
934   case eStateUnloaded:
935     return "Unloaded";
936   case eStateAttaching:
937     return "Attaching";
938   case eStateLaunching:
939     return "Launching";
940   case eStateStopped:
941     return "Stopped";
942   case eStateRunning:
943     return "Running";
944   case eStateStepping:
945     return "Stepping";
946   case eStateCrashed:
947     return "Crashed";
948   case eStateDetached:
949     return "Detached";
950   case eStateExited:
951     return "Exited";
952   case eStateSuspended:
953     return "Suspended";
954   }
955   return "nub_state_t ???";
956 }
957 
DNBGetGenealogyInfoForThread(nub_process_t pid,nub_thread_t tid,bool & timed_out)958 Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
959                                                          nub_thread_t tid,
960                                                          bool &timed_out) {
961   Genealogy::ThreadActivitySP thread_activity_sp;
962   MachProcessSP procSP;
963   if (GetProcessSP(pid, procSP))
964     thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);
965   return thread_activity_sp;
966 }
967 
DNBGetGenealogyImageInfo(nub_process_t pid,size_t idx)968 Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
969                                                             size_t idx) {
970   Genealogy::ProcessExecutableInfoSP image_info_sp;
971   MachProcessSP procSP;
972   if (GetProcessSP(pid, procSP)) {
973     image_info_sp = procSP->GetGenealogyImageInfo(idx);
974   }
975   return image_info_sp;
976 }
977 
DNBGetRequestedQoSForThread(nub_process_t pid,nub_thread_t tid,nub_addr_t tsd,uint64_t dti_qos_class_index)978 ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
979                                             nub_addr_t tsd,
980                                             uint64_t dti_qos_class_index) {
981   MachProcessSP procSP;
982   if (GetProcessSP(pid, procSP)) {
983     return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);
984   }
985   return ThreadInfo::QoS();
986 }
987 
DNBGetPThreadT(nub_process_t pid,nub_thread_t tid)988 nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {
989   MachProcessSP procSP;
990   if (GetProcessSP(pid, procSP)) {
991     return procSP->GetPThreadT(tid);
992   }
993   return INVALID_NUB_ADDRESS;
994 }
995 
DNBGetDispatchQueueT(nub_process_t pid,nub_thread_t tid)996 nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {
997   MachProcessSP procSP;
998   if (GetProcessSP(pid, procSP)) {
999     return procSP->GetDispatchQueueT(tid);
1000   }
1001   return INVALID_NUB_ADDRESS;
1002 }
1003 
1004 nub_addr_t
DNBGetTSDAddressForThread(nub_process_t pid,nub_thread_t tid,uint64_t plo_pthread_tsd_base_address_offset,uint64_t plo_pthread_tsd_base_offset,uint64_t plo_pthread_tsd_entry_size)1005 DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
1006                           uint64_t plo_pthread_tsd_base_address_offset,
1007                           uint64_t plo_pthread_tsd_base_offset,
1008                           uint64_t plo_pthread_tsd_entry_size) {
1009   MachProcessSP procSP;
1010   if (GetProcessSP(pid, procSP)) {
1011     return procSP->GetTSDAddressForThread(
1012         tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
1013         plo_pthread_tsd_entry_size);
1014   }
1015   return INVALID_NUB_ADDRESS;
1016 }
1017 
DNBGetLoadedDynamicLibrariesInfos(nub_process_t pid,nub_addr_t image_list_address,nub_addr_t image_count)1018 JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
1019     nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
1020   MachProcessSP procSP;
1021   if (GetProcessSP(pid, procSP)) {
1022     return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
1023                                                   image_count);
1024   }
1025   return JSONGenerator::ObjectSP();
1026 }
1027 
DNBGetAllLoadedLibrariesInfos(nub_process_t pid)1028 JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
1029   MachProcessSP procSP;
1030   if (GetProcessSP(pid, procSP)) {
1031     return procSP->GetAllLoadedLibrariesInfos(pid);
1032   }
1033   return JSONGenerator::ObjectSP();
1034 }
1035 
1036 JSONGenerator::ObjectSP
DNBGetLibrariesInfoForAddresses(nub_process_t pid,std::vector<uint64_t> & macho_addresses)1037 DNBGetLibrariesInfoForAddresses(nub_process_t pid,
1038                                 std::vector<uint64_t> &macho_addresses) {
1039   MachProcessSP procSP;
1040   if (GetProcessSP(pid, procSP)) {
1041     return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);
1042   }
1043   return JSONGenerator::ObjectSP();
1044 }
1045 
DNBGetSharedCacheInfo(nub_process_t pid)1046 JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {
1047   MachProcessSP procSP;
1048   if (GetProcessSP(pid, procSP)) {
1049     return procSP->GetSharedCacheInfo(pid);
1050   }
1051   return JSONGenerator::ObjectSP();
1052 }
1053 
DNBProcessGetExecutablePath(nub_process_t pid)1054 const char *DNBProcessGetExecutablePath(nub_process_t pid) {
1055   MachProcessSP procSP;
1056   if (GetProcessSP(pid, procSP)) {
1057     return procSP->Path();
1058   }
1059   return NULL;
1060 }
1061 
DNBProcessGetArgumentCount(nub_process_t pid)1062 nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {
1063   MachProcessSP procSP;
1064   if (GetProcessSP(pid, procSP)) {
1065     return procSP->ArgumentCount();
1066   }
1067   return 0;
1068 }
1069 
DNBProcessGetArgumentAtIndex(nub_process_t pid,nub_size_t idx)1070 const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {
1071   MachProcessSP procSP;
1072   if (GetProcessSP(pid, procSP)) {
1073     return procSP->ArgumentAtIndex(idx);
1074   }
1075   return NULL;
1076 }
1077 
1078 // Execution control
DNBProcessResume(nub_process_t pid,const DNBThreadResumeAction * actions,size_t num_actions)1079 nub_bool_t DNBProcessResume(nub_process_t pid,
1080                             const DNBThreadResumeAction *actions,
1081                             size_t num_actions) {
1082   DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1083   MachProcessSP procSP;
1084   if (GetProcessSP(pid, procSP)) {
1085     DNBThreadResumeActions thread_actions(actions, num_actions);
1086 
1087     // Below we add a default thread plan just in case one wasn't
1088     // provided so all threads always know what they were supposed to do
1089     if (thread_actions.IsEmpty()) {
1090       // No thread plans were given, so the default it to run all threads
1091       thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
1092     } else {
1093       // Some thread plans were given which means anything that wasn't
1094       // specified should remain stopped.
1095       thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
1096     }
1097     return procSP->Resume(thread_actions);
1098   }
1099   return false;
1100 }
1101 
DNBProcessHalt(nub_process_t pid)1102 nub_bool_t DNBProcessHalt(nub_process_t pid) {
1103   DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
1104   MachProcessSP procSP;
1105   if (GetProcessSP(pid, procSP))
1106     return procSP->Signal(SIGSTOP);
1107   return false;
1108 }
1109 //
1110 // nub_bool_t
1111 // DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
1112 //{
1113 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",
1114 //    __FUNCTION__, pid, tid, (uint32_t)step);
1115 //    MachProcessSP procSP;
1116 //    if (GetProcessSP (pid, procSP))
1117 //    {
1118 //        return procSP->Resume(tid, step, 0);
1119 //    }
1120 //    return false;
1121 //}
1122 //
1123 // nub_bool_t
1124 // DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t
1125 // step, int signal)
1126 //{
1127 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,
1128 //    signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
1129 //    MachProcessSP procSP;
1130 //    if (GetProcessSP (pid, procSP))
1131 //    {
1132 //        return procSP->Resume(tid, step, signal);
1133 //    }
1134 //    return false;
1135 //}
1136 
DNBProcessWaitForEvents(nub_process_t pid,nub_event_t event_mask,bool wait_for_set,struct timespec * timeout)1137 nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
1138                                     bool wait_for_set,
1139                                     struct timespec *timeout) {
1140   nub_event_t result = 0;
1141   MachProcessSP procSP;
1142   if (GetProcessSP(pid, procSP)) {
1143     if (wait_for_set)
1144       result = procSP->Events().WaitForSetEvents(event_mask, timeout);
1145     else
1146       result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
1147   }
1148   return result;
1149 }
1150 
DNBProcessResetEvents(nub_process_t pid,nub_event_t event_mask)1151 void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {
1152   MachProcessSP procSP;
1153   if (GetProcessSP(pid, procSP))
1154     procSP->Events().ResetEvents(event_mask);
1155 }
1156 
1157 // Breakpoints
DNBBreakpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,nub_bool_t hardware)1158 nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1159                             nub_bool_t hardware) {
1160   MachProcessSP procSP;
1161   if (GetProcessSP(pid, procSP))
1162     return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
1163   return false;
1164 }
1165 
DNBBreakpointClear(nub_process_t pid,nub_addr_t addr)1166 nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {
1167   MachProcessSP procSP;
1168   if (GetProcessSP(pid, procSP))
1169     return procSP->DisableBreakpoint(addr, true);
1170   return false; // Failed
1171 }
1172 
1173 // Watchpoints
DNBWatchpointSet(nub_process_t pid,nub_addr_t addr,nub_size_t size,uint32_t watch_flags,nub_bool_t hardware)1174 nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
1175                             uint32_t watch_flags, nub_bool_t hardware) {
1176   MachProcessSP procSP;
1177   if (GetProcessSP(pid, procSP))
1178     return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
1179   return false;
1180 }
1181 
DNBWatchpointClear(nub_process_t pid,nub_addr_t addr)1182 nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {
1183   MachProcessSP procSP;
1184   if (GetProcessSP(pid, procSP))
1185     return procSP->DisableWatchpoint(addr, true);
1186   return false; // Failed
1187 }
1188 
1189 // Return the number of supported hardware watchpoints.
DNBWatchpointGetNumSupportedHWP(nub_process_t pid)1190 uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {
1191   MachProcessSP procSP;
1192   if (GetProcessSP(pid, procSP))
1193     return procSP->GetNumSupportedHardwareWatchpoints();
1194   return 0;
1195 }
1196 
1197 // Read memory in the address space of process PID. This call will take
1198 // care of setting and restoring permissions and breaking up the memory
1199 // read into multiple chunks as required.
1200 //
1201 // RETURNS: number of bytes actually read
DNBProcessMemoryRead(nub_process_t pid,nub_addr_t addr,nub_size_t size,void * buf)1202 nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
1203                                 nub_size_t size, void *buf) {
1204   MachProcessSP procSP;
1205   if (GetProcessSP(pid, procSP))
1206     return procSP->ReadMemory(addr, size, buf);
1207   return 0;
1208 }
1209 
DNBProcessMemoryReadInteger(nub_process_t pid,nub_addr_t addr,nub_size_t integer_size,uint64_t fail_value)1210 uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
1211                                      nub_size_t integer_size,
1212                                      uint64_t fail_value) {
1213   union Integers {
1214     uint8_t u8;
1215     uint16_t u16;
1216     uint32_t u32;
1217     uint64_t u64;
1218   };
1219 
1220   if (integer_size <= sizeof(uint64_t)) {
1221     Integers ints;
1222     if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {
1223       switch (integer_size) {
1224       case 1:
1225         return ints.u8;
1226       case 2:
1227         return ints.u16;
1228       case 3:
1229         return ints.u32 & 0xffffffu;
1230       case 4:
1231         return ints.u32;
1232       case 5:
1233         return ints.u32 & 0x000000ffffffffffull;
1234       case 6:
1235         return ints.u32 & 0x0000ffffffffffffull;
1236       case 7:
1237         return ints.u32 & 0x00ffffffffffffffull;
1238       case 8:
1239         return ints.u64;
1240       }
1241     }
1242   }
1243   return fail_value;
1244 }
1245 
DNBProcessMemoryReadPointer(nub_process_t pid,nub_addr_t addr)1246 nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {
1247   cpu_type_t cputype = DNBProcessGetCPUType(pid);
1248   if (cputype) {
1249     const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
1250     return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
1251   }
1252   return 0;
1253 }
1254 
DNBProcessMemoryReadCString(nub_process_t pid,nub_addr_t addr)1255 std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {
1256   std::string cstr;
1257   char buffer[256];
1258   const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;
1259   buffer[max_buffer_cstr_length] = '\0';
1260   nub_size_t length = 0;
1261   nub_addr_t curr_addr = addr;
1262   do {
1263     nub_size_t bytes_read =
1264         DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
1265     if (bytes_read == 0)
1266       break;
1267     length = strlen(buffer);
1268     cstr.append(buffer, length);
1269     curr_addr += length;
1270   } while (length == max_buffer_cstr_length);
1271   return cstr;
1272 }
1273 
DNBProcessMemoryReadCStringFixed(nub_process_t pid,nub_addr_t addr,nub_size_t fixed_length)1274 std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
1275                                              nub_size_t fixed_length) {
1276   std::string cstr;
1277   char buffer[fixed_length + 1];
1278   buffer[fixed_length] = '\0';
1279   nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
1280   if (bytes_read > 0)
1281     cstr.assign(buffer);
1282   return cstr;
1283 }
1284 
1285 // Write memory to the address space of process PID. This call will take
1286 // care of setting and restoring permissions and breaking up the memory
1287 // write into multiple chunks as required.
1288 //
1289 // RETURNS: number of bytes actually written
DNBProcessMemoryWrite(nub_process_t pid,nub_addr_t addr,nub_size_t size,const void * buf)1290 nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
1291                                  nub_size_t size, const void *buf) {
1292   MachProcessSP procSP;
1293   if (GetProcessSP(pid, procSP))
1294     return procSP->WriteMemory(addr, size, buf);
1295   return 0;
1296 }
1297 
DNBProcessMemoryAllocate(nub_process_t pid,nub_size_t size,uint32_t permissions)1298 nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
1299                                     uint32_t permissions) {
1300   MachProcessSP procSP;
1301   if (GetProcessSP(pid, procSP))
1302     return procSP->Task().AllocateMemory(size, permissions);
1303   return 0;
1304 }
1305 
DNBProcessMemoryDeallocate(nub_process_t pid,nub_addr_t addr)1306 nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {
1307   MachProcessSP procSP;
1308   if (GetProcessSP(pid, procSP))
1309     return procSP->Task().DeallocateMemory(addr);
1310   return 0;
1311 }
1312 
1313 // Find attributes of the memory region that contains ADDR for process PID,
1314 // if possible, and return a string describing those attributes.
1315 //
1316 // Returns 1 if we could find attributes for this region and OUTBUF can
1317 // be sent to the remote debugger.
1318 //
1319 // Returns 0 if we couldn't find the attributes for a region of memory at
1320 // that address and OUTBUF should not be sent.
1321 //
1322 // Returns -1 if this platform cannot look up information about memory regions
1323 // or if we do not yet have a valid launched process.
1324 //
DNBProcessMemoryRegionInfo(nub_process_t pid,nub_addr_t addr,DNBRegionInfo * region_info)1325 int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
1326                                DNBRegionInfo *region_info) {
1327   MachProcessSP procSP;
1328   if (GetProcessSP(pid, procSP))
1329     return procSP->Task().GetMemoryRegionInfo(addr, region_info);
1330 
1331   return -1;
1332 }
1333 
DNBProcessGetProfileData(nub_process_t pid,DNBProfileDataScanType scanType)1334 std::string DNBProcessGetProfileData(nub_process_t pid,
1335                                      DNBProfileDataScanType scanType) {
1336   MachProcessSP procSP;
1337   if (GetProcessSP(pid, procSP))
1338     return procSP->Task().GetProfileData(scanType);
1339 
1340   return std::string("");
1341 }
1342 
DNBProcessSetEnableAsyncProfiling(nub_process_t pid,nub_bool_t enable,uint64_t interval_usec,DNBProfileDataScanType scan_type)1343 nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,
1344                                              nub_bool_t enable,
1345                                              uint64_t interval_usec,
1346                                              DNBProfileDataScanType scan_type) {
1347   MachProcessSP procSP;
1348   if (GetProcessSP(pid, procSP)) {
1349     procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
1350     return true;
1351   }
1352 
1353   return false;
1354 }
1355 
1356 // Get the number of threads for the specified process.
DNBProcessGetNumThreads(nub_process_t pid)1357 nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {
1358   MachProcessSP procSP;
1359   if (GetProcessSP(pid, procSP))
1360     return procSP->GetNumThreads();
1361   return 0;
1362 }
1363 
1364 // Get the thread ID of the current thread.
DNBProcessGetCurrentThread(nub_process_t pid)1365 nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {
1366   MachProcessSP procSP;
1367   if (GetProcessSP(pid, procSP))
1368     return procSP->GetCurrentThread();
1369   return 0;
1370 }
1371 
1372 // Get the mach port number of the current thread.
DNBProcessGetCurrentThreadMachPort(nub_process_t pid)1373 nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {
1374   MachProcessSP procSP;
1375   if (GetProcessSP(pid, procSP))
1376     return procSP->GetCurrentThreadMachPort();
1377   return 0;
1378 }
1379 
1380 // Change the current thread.
DNBProcessSetCurrentThread(nub_process_t pid,nub_thread_t tid)1381 nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {
1382   MachProcessSP procSP;
1383   if (GetProcessSP(pid, procSP))
1384     return procSP->SetCurrentThread(tid);
1385   return INVALID_NUB_THREAD;
1386 }
1387 
1388 // Dump a string describing a thread's stop reason to the specified file
1389 // handle
DNBThreadGetStopReason(nub_process_t pid,nub_thread_t tid,struct DNBThreadStopInfo * stop_info)1390 nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
1391                                   struct DNBThreadStopInfo *stop_info) {
1392   MachProcessSP procSP;
1393   if (GetProcessSP(pid, procSP))
1394     return procSP->GetThreadStoppedReason(tid, stop_info);
1395   return false;
1396 }
1397 
1398 // Return string description for the specified thread.
1399 //
1400 // RETURNS: NULL if the thread isn't valid, else a NULL terminated C
1401 // string from a static buffer that must be copied prior to subsequent
1402 // calls.
DNBThreadGetInfo(nub_process_t pid,nub_thread_t tid)1403 const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {
1404   MachProcessSP procSP;
1405   if (GetProcessSP(pid, procSP))
1406     return procSP->GetThreadInfo(tid);
1407   return NULL;
1408 }
1409 
1410 // Get the thread ID given a thread index.
DNBProcessGetThreadAtIndex(nub_process_t pid,size_t thread_idx)1411 nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {
1412   MachProcessSP procSP;
1413   if (GetProcessSP(pid, procSP))
1414     return procSP->GetThreadAtIndex(thread_idx);
1415   return INVALID_NUB_THREAD;
1416 }
1417 
1418 // Do whatever is needed to sync the thread's register state with it's kernel
1419 // values.
DNBProcessSyncThreadState(nub_process_t pid,nub_thread_t tid)1420 nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {
1421   MachProcessSP procSP;
1422   if (GetProcessSP(pid, procSP))
1423     return procSP->SyncThreadState(tid);
1424   return false;
1425 }
1426 
DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid)1427 nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {
1428   MachProcessSP procSP;
1429   DNBError err;
1430   if (GetProcessSP(pid, procSP))
1431     return procSP->Task().GetDYLDAllImageInfosAddress(err);
1432   return INVALID_NUB_ADDRESS;
1433 }
1434 
DNBProcessSharedLibrariesUpdated(nub_process_t pid)1435 nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
1436   MachProcessSP procSP;
1437   if (GetProcessSP(pid, procSP)) {
1438     procSP->SharedLibrariesUpdated();
1439     return true;
1440   }
1441   return false;
1442 }
1443 
1444 std::optional<std::string>
DNBGetDeploymentInfo(nub_process_t pid,bool is_executable,const struct load_command & lc,uint64_t load_command_address,uint32_t & major_version,uint32_t & minor_version,uint32_t & patch_version)1445 DNBGetDeploymentInfo(nub_process_t pid, bool is_executable,
1446                      const struct load_command &lc,
1447                      uint64_t load_command_address, uint32_t &major_version,
1448                      uint32_t &minor_version, uint32_t &patch_version) {
1449   MachProcessSP procSP;
1450   if (GetProcessSP(pid, procSP)) {
1451     // FIXME: This doesn't return the correct result when xctest (a
1452     // macOS binary) is loaded with the macCatalyst dyld platform
1453     // override. The image info corrects for this, but qProcessInfo
1454     // will return what is in the binary.
1455     auto info =
1456         procSP->GetDeploymentInfo(lc, load_command_address, is_executable);
1457     major_version = info.major_version;
1458     minor_version = info.minor_version;
1459     patch_version = info.patch_version;
1460     return procSP->GetPlatformString(info.platform);
1461   }
1462   return nullptr;
1463 }
1464 
1465 // Get the current shared library information for a process. Only return
1466 // the shared libraries that have changed since the last shared library
1467 // state changed event if only_changed is non-zero.
1468 nub_size_t
DNBProcessGetSharedLibraryInfo(nub_process_t pid,nub_bool_t only_changed,struct DNBExecutableImageInfo ** image_infos)1469 DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
1470                                struct DNBExecutableImageInfo **image_infos) {
1471   MachProcessSP procSP;
1472   if (GetProcessSP(pid, procSP))
1473     return procSP->CopyImageInfos(image_infos, only_changed);
1474 
1475   // If we have no process, then return NULL for the shared library info
1476   // and zero for shared library count
1477   *image_infos = NULL;
1478   return 0;
1479 }
1480 
DNBGetRegisterCPUType()1481 uint32_t DNBGetRegisterCPUType() {
1482   return DNBArchProtocol::GetRegisterCPUType();
1483 }
1484 // Get the register set information for a specific thread.
DNBGetRegisterSetInfo(nub_size_t * num_reg_sets)1485 const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {
1486   return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);
1487 }
1488 
1489 // Read a register value by register set and register index.
DNBThreadGetRegisterValueByID(nub_process_t pid,nub_thread_t tid,uint32_t set,uint32_t reg,DNBRegisterValue * value)1490 nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1491                                          uint32_t set, uint32_t reg,
1492                                          DNBRegisterValue *value) {
1493   MachProcessSP procSP;
1494   ::bzero(value, sizeof(DNBRegisterValue));
1495   if (GetProcessSP(pid, procSP)) {
1496     if (tid != INVALID_NUB_THREAD)
1497       return procSP->GetRegisterValue(tid, set, reg, value);
1498   }
1499   return false;
1500 }
1501 
DNBThreadSetRegisterValueByID(nub_process_t pid,nub_thread_t tid,uint32_t set,uint32_t reg,const DNBRegisterValue * value)1502 nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
1503                                          uint32_t set, uint32_t reg,
1504                                          const DNBRegisterValue *value) {
1505   if (tid != INVALID_NUB_THREAD) {
1506     MachProcessSP procSP;
1507     if (GetProcessSP(pid, procSP))
1508       return procSP->SetRegisterValue(tid, set, reg, value);
1509   }
1510   return false;
1511 }
1512 
DNBThreadGetRegisterContext(nub_process_t pid,nub_thread_t tid,void * buf,size_t buf_len)1513 nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
1514                                        void *buf, size_t buf_len) {
1515   MachProcessSP procSP;
1516   if (GetProcessSP(pid, procSP)) {
1517     if (tid != INVALID_NUB_THREAD)
1518       return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);
1519   }
1520   ::bzero(buf, buf_len);
1521   return 0;
1522 }
1523 
DNBThreadSetRegisterContext(nub_process_t pid,nub_thread_t tid,const void * buf,size_t buf_len)1524 nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
1525                                        const void *buf, size_t buf_len) {
1526   MachProcessSP procSP;
1527   if (GetProcessSP(pid, procSP)) {
1528     if (tid != INVALID_NUB_THREAD)
1529       return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);
1530   }
1531   return 0;
1532 }
1533 
DNBThreadSaveRegisterState(nub_process_t pid,nub_thread_t tid)1534 uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {
1535   if (tid != INVALID_NUB_THREAD) {
1536     MachProcessSP procSP;
1537     if (GetProcessSP(pid, procSP))
1538       return procSP->GetThreadList().SaveRegisterState(tid);
1539   }
1540   return 0;
1541 }
DNBThreadRestoreRegisterState(nub_process_t pid,nub_thread_t tid,uint32_t save_id)1542 nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
1543                                          uint32_t save_id) {
1544   if (tid != INVALID_NUB_THREAD) {
1545     MachProcessSP procSP;
1546     if (GetProcessSP(pid, procSP))
1547       return procSP->GetThreadList().RestoreRegisterState(tid, save_id);
1548   }
1549   return false;
1550 }
1551 
1552 // Read a register value by name.
DNBThreadGetRegisterValueByName(nub_process_t pid,nub_thread_t tid,uint32_t reg_set,const char * reg_name,DNBRegisterValue * value)1553 nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
1554                                            uint32_t reg_set,
1555                                            const char *reg_name,
1556                                            DNBRegisterValue *value) {
1557   MachProcessSP procSP;
1558   ::bzero(value, sizeof(DNBRegisterValue));
1559   if (GetProcessSP(pid, procSP)) {
1560     const struct DNBRegisterSetInfo *set_info;
1561     nub_size_t num_reg_sets = 0;
1562     set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1563     if (set_info) {
1564       uint32_t set = reg_set;
1565       uint32_t reg;
1566       if (set == REGISTER_SET_ALL) {
1567         for (set = 1; set < num_reg_sets; ++set) {
1568           for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1569             if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1570               return procSP->GetRegisterValue(tid, set, reg, value);
1571           }
1572         }
1573       } else {
1574         for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1575           if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1576             return procSP->GetRegisterValue(tid, set, reg, value);
1577         }
1578       }
1579     }
1580   }
1581   return false;
1582 }
1583 
1584 // Read a register set and register number from the register name.
DNBGetRegisterInfoByName(const char * reg_name,DNBRegisterInfo * info)1585 nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
1586                                     DNBRegisterInfo *info) {
1587   const struct DNBRegisterSetInfo *set_info;
1588   nub_size_t num_reg_sets = 0;
1589   set_info = DNBGetRegisterSetInfo(&num_reg_sets);
1590   if (set_info) {
1591     uint32_t set, reg;
1592     for (set = 1; set < num_reg_sets; ++set) {
1593       for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1594         if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {
1595           *info = set_info[set].registers[reg];
1596           return true;
1597         }
1598       }
1599     }
1600 
1601     for (set = 1; set < num_reg_sets; ++set) {
1602       uint32_t reg;
1603       for (reg = 0; reg < set_info[set].num_registers; ++reg) {
1604         if (set_info[set].registers[reg].alt == NULL)
1605           continue;
1606 
1607         if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {
1608           *info = set_info[set].registers[reg];
1609           return true;
1610         }
1611       }
1612     }
1613   }
1614 
1615   ::bzero(info, sizeof(DNBRegisterInfo));
1616   return false;
1617 }
1618 
1619 // Set the name to address callback function that this nub can use
1620 // for any name to address lookups that are needed.
DNBProcessSetNameToAddressCallback(nub_process_t pid,DNBCallbackNameToAddress callback,void * baton)1621 nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
1622                                               DNBCallbackNameToAddress callback,
1623                                               void *baton) {
1624   MachProcessSP procSP;
1625   if (GetProcessSP(pid, procSP)) {
1626     procSP->SetNameToAddressCallback(callback, baton);
1627     return true;
1628   }
1629   return false;
1630 }
1631 
1632 // Set the name to address callback function that this nub can use
1633 // for any name to address lookups that are needed.
DNBProcessSetSharedLibraryInfoCallback(nub_process_t pid,DNBCallbackCopyExecutableImageInfos callback,void * baton)1634 nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
1635     nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
1636     void *baton) {
1637   MachProcessSP procSP;
1638   if (GetProcessSP(pid, procSP)) {
1639     procSP->SetSharedLibraryInfoCallback(callback, baton);
1640     return true;
1641   }
1642   return false;
1643 }
1644 
DNBProcessLookupAddress(nub_process_t pid,const char * name,const char * shlib)1645 nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
1646                                    const char *shlib) {
1647   MachProcessSP procSP;
1648   if (GetProcessSP(pid, procSP)) {
1649     return procSP->LookupSymbol(name, shlib);
1650   }
1651   return INVALID_NUB_ADDRESS;
1652 }
1653 
DNBProcessGetAvailableSTDOUT(nub_process_t pid,char * buf,nub_size_t buf_size)1654 nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
1655                                         nub_size_t buf_size) {
1656   MachProcessSP procSP;
1657   if (GetProcessSP(pid, procSP))
1658     return procSP->GetAvailableSTDOUT(buf, buf_size);
1659   return 0;
1660 }
1661 
DNBProcessGetAvailableSTDERR(nub_process_t pid,char * buf,nub_size_t buf_size)1662 nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
1663                                         nub_size_t buf_size) {
1664   MachProcessSP procSP;
1665   if (GetProcessSP(pid, procSP))
1666     return procSP->GetAvailableSTDERR(buf, buf_size);
1667   return 0;
1668 }
1669 
DNBProcessGetAvailableProfileData(nub_process_t pid,char * buf,nub_size_t buf_size)1670 nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
1671                                              nub_size_t buf_size) {
1672   MachProcessSP procSP;
1673   if (GetProcessSP(pid, procSP))
1674     return procSP->GetAsyncProfileData(buf, buf_size);
1675   return 0;
1676 }
1677 
DNBProcessGetStopCount(nub_process_t pid)1678 nub_size_t DNBProcessGetStopCount(nub_process_t pid) {
1679   MachProcessSP procSP;
1680   if (GetProcessSP(pid, procSP))
1681     return procSP->StopCount();
1682   return 0;
1683 }
1684 
DNBProcessGetCPUType(nub_process_t pid)1685 uint32_t DNBProcessGetCPUType(nub_process_t pid) {
1686   MachProcessSP procSP;
1687   if (GetProcessSP(pid, procSP))
1688     return procSP->GetCPUType();
1689   return 0;
1690 }
1691 
DNBResolveExecutablePath(const char * path,char * resolved_path,size_t resolved_path_size)1692 nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
1693                                     size_t resolved_path_size) {
1694   if (path == NULL || path[0] == '\0')
1695     return false;
1696 
1697   char max_path[PATH_MAX];
1698   std::string result;
1699   CFString::GlobPath(path, result);
1700 
1701   if (result.empty())
1702     result = path;
1703 
1704   struct stat path_stat;
1705   if (::stat(path, &path_stat) == 0) {
1706     if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
1707       CFBundle bundle(path);
1708       CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
1709       if (url.get()) {
1710         if (::CFURLGetFileSystemRepresentation(
1711                 url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
1712           return true;
1713       }
1714     }
1715   }
1716 
1717   if (realpath(path, max_path)) {
1718     // Found the path relatively...
1719     ::strlcpy(resolved_path, max_path, resolved_path_size);
1720     return strlen(resolved_path) + 1 < resolved_path_size;
1721   } else {
1722     // Not a relative path, check the PATH environment variable if the
1723     const char *PATH = getenv("PATH");
1724     if (PATH) {
1725       const char *curr_path_start = PATH;
1726       const char *curr_path_end;
1727       while (curr_path_start && *curr_path_start) {
1728         curr_path_end = strchr(curr_path_start, ':');
1729         if (curr_path_end == NULL) {
1730           result.assign(curr_path_start);
1731           curr_path_start = NULL;
1732         } else if (curr_path_end > curr_path_start) {
1733           size_t len = curr_path_end - curr_path_start;
1734           result.assign(curr_path_start, len);
1735           curr_path_start += len + 1;
1736         } else
1737           break;
1738 
1739         result += '/';
1740         result += path;
1741         struct stat s;
1742         if (stat(result.c_str(), &s) == 0) {
1743           ::strlcpy(resolved_path, result.c_str(), resolved_path_size);
1744           return result.size() + 1 < resolved_path_size;
1745         }
1746       }
1747     }
1748   }
1749   return false;
1750 }
1751 
DNBGetOSVersionNumbers(uint64_t * major,uint64_t * minor,uint64_t * patch)1752 bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {
1753   return MachProcess::GetOSVersionNumbers(major, minor, patch);
1754 }
1755 
DNBGetMacCatalystVersionString()1756 std::string DNBGetMacCatalystVersionString() {
1757   return MachProcess::GetMacCatalystVersionString();
1758 }
1759 
DNBInitialize()1760 void DNBInitialize() {
1761   DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");
1762 #if defined(__i386__) || defined(__x86_64__)
1763   DNBArchImplI386::Initialize();
1764   DNBArchImplX86_64::Initialize();
1765 #elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
1766   DNBArchMachARM::Initialize();
1767   DNBArchMachARM64::Initialize();
1768 #endif
1769 }
1770 
DNBTerminate()1771 void DNBTerminate() {}
1772 
DNBSetArchitecture(const char * arch)1773 nub_bool_t DNBSetArchitecture(const char *arch) {
1774   if (arch && arch[0]) {
1775     if (strcasecmp(arch, "i386") == 0)
1776       return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
1777     else if (strcasecmp(arch, "x86_64") == 0)
1778       return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,
1779                                               CPU_SUBTYPE_X86_64_ALL);
1780     else if (strcasecmp(arch, "x86_64h") == 0)
1781       return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64,
1782                                               CPU_SUBTYPE_X86_64_H);
1783     else if (strstr(arch, "arm64_32") == arch ||
1784              strstr(arch, "aarch64_32") == arch)
1785       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
1786     else if (strstr(arch, "arm64e") == arch)
1787       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1788                                               CPU_SUBTYPE_ARM64E);
1789     else if (strstr(arch, "arm64") == arch || strstr(arch, "aarch64") == arch)
1790       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1791                                               CPU_SUBTYPE_ARM64_ALL);
1792     else if (strstr(arch, "armv8") == arch)
1793       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64,
1794                                               CPU_SUBTYPE_ARM64_V8);
1795     else if (strstr(arch, "armv7em") == arch)
1796       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1797                                               CPU_SUBTYPE_ARM_V7EM);
1798     else if (strstr(arch, "armv7m") == arch)
1799       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1800                                               CPU_SUBTYPE_ARM_V7M);
1801     else if (strstr(arch, "armv7k") == arch)
1802       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1803                                               CPU_SUBTYPE_ARM_V7K);
1804     else if (strstr(arch, "armv7s") == arch)
1805       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1806                                               CPU_SUBTYPE_ARM_V7S);
1807     else if (strstr(arch, "armv7") == arch)
1808       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7);
1809     else if (strstr(arch, "armv6m") == arch)
1810       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1811                                               CPU_SUBTYPE_ARM_V6M);
1812     else if (strstr(arch, "armv6") == arch)
1813       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6);
1814     else if (strstr(arch, "armv5") == arch)
1815       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1816                                               CPU_SUBTYPE_ARM_V5TEJ);
1817     else if (strstr(arch, "armv4t") == arch)
1818       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1819                                               CPU_SUBTYPE_ARM_V4T);
1820     else if (strstr(arch, "arm") == arch)
1821       return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM,
1822                                               CPU_SUBTYPE_ARM_ALL);
1823   }
1824   return false;
1825 }
1826 
DNBDebugserverIsTranslated()1827 bool DNBDebugserverIsTranslated() {
1828   int ret = 0;
1829   size_t size = sizeof(ret);
1830   if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)
1831     return false;
1832   return ret == 1;
1833 }
1834 
DNBGetAddressingBits(uint32_t & addressing_bits)1835 bool DNBGetAddressingBits(uint32_t &addressing_bits) {
1836   static uint32_t g_addressing_bits = 0;
1837   static std::once_flag g_once_flag;
1838   std::call_once(g_once_flag, [&](){
1839     size_t len = sizeof(uint32_t);
1840     if (::sysctlbyname("machdep.virtual_address_size", &g_addressing_bits, &len,
1841                        NULL, 0) != 0) {
1842       g_addressing_bits = 0;
1843     }
1844   });
1845 
1846   addressing_bits = g_addressing_bits;
1847 
1848   return addressing_bits > 0;
1849 }
1850