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