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