1 //===-- DNB.cpp -------------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  Created by Greg Clayton on 3/23/07.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "DNB.h"
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <sys/resource.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <sys/sysctl.h>
24 #include <map>
25 #include <vector>
26 #include <libproc.h>
27 
28 #include "MacOSX/MachProcess.h"
29 #include "MacOSX/MachTask.h"
30 #include "CFString.h"
31 #include "DNBLog.h"
32 #include "DNBDataRef.h"
33 #include "DNBThreadResumeActions.h"
34 #include "DNBTimer.h"
35 #include "CFBundle.h"
36 
37 
38 typedef STD_SHARED_PTR(MachProcess) MachProcessSP;
39 typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
40 typedef ProcessMap::iterator ProcessMapIter;
41 typedef ProcessMap::const_iterator ProcessMapConstIter;
42 
43 size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos);
44 static size_t GetAllInfosMatchingName (const char *process_name, std::vector<struct kinfo_proc>& matching_proc_infos);
45 
46 //----------------------------------------------------------------------
47 // A Thread safe singleton to get a process map pointer.
48 //
49 // Returns a pointer to the existing process map, or a pointer to a
50 // newly created process map if CAN_CREATE is non-zero.
51 //----------------------------------------------------------------------
52 static ProcessMap*
53 GetProcessMap(bool can_create)
54 {
55     static ProcessMap* g_process_map_ptr = NULL;
56 
57     if (can_create && g_process_map_ptr == NULL)
58     {
59         static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
60         PTHREAD_MUTEX_LOCKER (locker, &g_process_map_mutex);
61         if (g_process_map_ptr == NULL)
62             g_process_map_ptr = new ProcessMap;
63     }
64     return g_process_map_ptr;
65 }
66 
67 //----------------------------------------------------------------------
68 // Add PID to the shared process pointer map.
69 //
70 // Return non-zero value if we succeed in adding the process to the map.
71 // The only time this should fail is if we run out of memory and can't
72 // allocate a ProcessMap.
73 //----------------------------------------------------------------------
74 static nub_bool_t
75 AddProcessToMap (nub_process_t pid, MachProcessSP& procSP)
76 {
77     ProcessMap* process_map = GetProcessMap(true);
78     if (process_map)
79     {
80         process_map->insert(std::make_pair(pid, procSP));
81         return true;
82     }
83     return false;
84 }
85 
86 //----------------------------------------------------------------------
87 // Remove the shared pointer for PID from the process map.
88 //
89 // Returns the number of items removed from the process map.
90 //----------------------------------------------------------------------
91 static size_t
92 RemoveProcessFromMap (nub_process_t pid)
93 {
94     ProcessMap* process_map = GetProcessMap(false);
95     if (process_map)
96     {
97         return process_map->erase(pid);
98     }
99     return 0;
100 }
101 
102 //----------------------------------------------------------------------
103 // Get the shared pointer for PID from the existing process map.
104 //
105 // Returns true if we successfully find a shared pointer to a
106 // MachProcess object.
107 //----------------------------------------------------------------------
108 static nub_bool_t
109 GetProcessSP (nub_process_t pid, MachProcessSP& procSP)
110 {
111     ProcessMap* process_map = GetProcessMap(false);
112     if (process_map != NULL)
113     {
114         ProcessMapIter pos = process_map->find(pid);
115         if (pos != process_map->end())
116         {
117             procSP = pos->second;
118             return true;
119         }
120     }
121     procSP.reset();
122     return false;
123 }
124 
125 
126 static void *
127 waitpid_thread (void *arg)
128 {
129     const pid_t pid = (pid_t)(intptr_t)arg;
130     int status;
131     while (1)
132     {
133         pid_t child_pid = waitpid(pid, &status, 0);
134         DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): waitpid (pid = %i, &status, 0) => %i, status = %i, errno = %i", pid, child_pid, status, errno);
135 
136         if (child_pid < 0)
137         {
138             if (errno == EINTR)
139                 continue;
140             break;
141         }
142         else
143         {
144             if (WIFSTOPPED(status))
145             {
146                 continue;
147             }
148             else// if (WIFEXITED(status) || WIFSIGNALED(status))
149             {
150                 DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): setting exit status for pid = %i to %i", child_pid, status);
151                 DNBProcessSetExitStatus (child_pid, status);
152                 return NULL;
153             }
154         }
155     }
156 
157     // We should never exit as long as our child process is alive, so if we
158     // do something else went wrong and we should exit...
159     DNBLogThreadedIf(LOG_PROCESS, "waitpid_process_thread (): main loop exited, setting exit status to an invalid value (-1) for pid %i", pid);
160     DNBProcessSetExitStatus (pid, -1);
161     return NULL;
162 }
163 
164 static bool
165 spawn_waitpid_thread (pid_t pid)
166 {
167     pthread_t thread = THREAD_NULL;
168     ::pthread_create (&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
169     if (thread != THREAD_NULL)
170     {
171         ::pthread_detach (thread);
172         return true;
173     }
174     return false;
175 }
176 
177 nub_process_t
178 DNBProcessLaunch (const char *path,
179                   char const *argv[],
180                   const char *envp[],
181                   const char *working_directory, // NULL => dont' change, non-NULL => set working directory for inferior to this
182                   const char *stdin_path,
183                   const char *stdout_path,
184                   const char *stderr_path,
185                   bool no_stdio,
186                   nub_launch_flavor_t launch_flavor,
187                   int disable_aslr,
188                   char *err_str,
189                   size_t err_len)
190 {
191     DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, working_dir=%s, stdin=%s, stdout=%s, stderr=%s, no-stdio=%i, launch_flavor = %u, disable_aslr = %d, err = %p, err_len = %llu) called...",
192                      __FUNCTION__,
193                      path,
194                      argv,
195                      envp,
196                      working_directory,
197                      stdin_path,
198                      stdout_path,
199                      stderr_path,
200                      no_stdio,
201                      launch_flavor,
202                      disable_aslr,
203                      err_str,
204                      (uint64_t)err_len);
205 
206     if (err_str && err_len > 0)
207         err_str[0] = '\0';
208     struct stat path_stat;
209     if (::stat(path, &path_stat) == -1)
210     {
211         char stat_error[256];
212         ::strerror_r (errno, stat_error, sizeof(stat_error));
213         snprintf(err_str, err_len, "%s (%s)", stat_error, path);
214         return INVALID_NUB_PROCESS;
215     }
216 
217     MachProcessSP processSP (new MachProcess);
218     if (processSP.get())
219     {
220         DNBError launch_err;
221         pid_t pid = processSP->LaunchForDebug (path,
222                                                argv,
223                                                envp,
224                                                working_directory,
225                                                stdin_path,
226                                                stdout_path,
227                                                stderr_path,
228                                                no_stdio,
229                                                launch_flavor,
230                                                disable_aslr,
231                                                launch_err);
232         if (err_str)
233         {
234             *err_str = '\0';
235             if (launch_err.Fail())
236             {
237                 const char *launch_err_str = launch_err.AsString();
238                 if (launch_err_str)
239                 {
240                     strncpy(err_str, launch_err_str, err_len-1);
241                     err_str[err_len-1] = '\0';  // Make sure the error string is terminated
242                 }
243             }
244         }
245 
246         DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
247 
248         if (pid != INVALID_NUB_PROCESS)
249         {
250             // Spawn a thread to reap our child inferior process...
251             spawn_waitpid_thread (pid);
252 
253             if (processSP->Task().TaskPortForProcessID (launch_err) == TASK_NULL)
254             {
255                 // We failed to get the task for our process ID which is bad.
256                 // Kill our process otherwise it will be stopped at the entry
257                 // point and get reparented to someone else and never go away.
258                 DNBLog ("Could not get task port for process, sending SIGKILL and exiting.");
259                 kill (SIGKILL, pid);
260 
261                 if (err_str && err_len > 0)
262                 {
263                     if (launch_err.AsString())
264                     {
265                         ::snprintf (err_str, err_len, "failed to get the task for process %i (%s)", pid, launch_err.AsString());
266                     }
267                     else
268                     {
269                         ::snprintf (err_str, err_len, "failed to get the task for process %i", pid);
270                     }
271                 }
272             }
273             else
274             {
275                 bool res = AddProcessToMap(pid, processSP);
276                 assert(res && "Couldn't add process to map!");
277                 return pid;
278             }
279         }
280     }
281     return INVALID_NUB_PROCESS;
282 }
283 
284 nub_process_t
285 DNBProcessAttachByName (const char *name, struct timespec *timeout, char *err_str, size_t err_len)
286 {
287     if (err_str && err_len > 0)
288         err_str[0] = '\0';
289     std::vector<struct kinfo_proc> matching_proc_infos;
290     size_t num_matching_proc_infos = GetAllInfosMatchingName(name, matching_proc_infos);
291     if (num_matching_proc_infos == 0)
292     {
293         DNBLogError ("error: no processes match '%s'\n", name);
294         return INVALID_NUB_PROCESS;
295     }
296     else if (num_matching_proc_infos > 1)
297     {
298         DNBLogError ("error: %llu processes match '%s':\n", (uint64_t)num_matching_proc_infos, name);
299         size_t i;
300         for (i=0; i<num_matching_proc_infos; ++i)
301             DNBLogError ("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid, matching_proc_infos[i].kp_proc.p_comm);
302         return INVALID_NUB_PROCESS;
303     }
304 
305     return DNBProcessAttach (matching_proc_infos[0].kp_proc.p_pid, timeout, err_str, err_len);
306 }
307 
308 nub_process_t
309 DNBProcessAttach (nub_process_t attach_pid, struct timespec *timeout, char *err_str, size_t err_len)
310 {
311     if (err_str && err_len > 0)
312         err_str[0] = '\0';
313 
314     pid_t pid = INVALID_NUB_PROCESS;
315     MachProcessSP processSP(new MachProcess);
316     if (processSP.get())
317     {
318         DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...", attach_pid);
319         pid = processSP->AttachForDebug (attach_pid, err_str,  err_len);
320 
321         if (pid != INVALID_NUB_PROCESS)
322         {
323             bool res = AddProcessToMap(pid, processSP);
324             assert(res && "Couldn't add process to map!");
325             spawn_waitpid_thread(pid);
326         }
327     }
328 
329     while (pid != INVALID_NUB_PROCESS)
330     {
331         // Wait for process to start up and hit entry point
332         DNBLogThreadedIf (LOG_PROCESS,
333                           "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...",
334                           __FUNCTION__,
335                           pid);
336         nub_event_t set_events = DNBProcessWaitForEvents (pid,
337                                                           eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged,
338                                                           true,
339                                                           timeout);
340 
341         DNBLogThreadedIf (LOG_PROCESS,
342                           "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x",
343                           __FUNCTION__,
344                           pid,
345                           set_events);
346 
347         if (set_events == 0)
348         {
349             if (err_str && err_len > 0)
350                 snprintf(err_str, err_len, "operation timed out");
351             pid = INVALID_NUB_PROCESS;
352         }
353         else
354         {
355             if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
356             {
357                 nub_state_t pid_state = DNBProcessGetState (pid);
358                 DNBLogThreadedIf (LOG_PROCESS, "%s process %4.4x state changed (eEventProcessStateChanged): %s",
359                         __FUNCTION__, pid, DNBStateAsString(pid_state));
360 
361                 switch (pid_state)
362                 {
363                     default:
364                     case eStateInvalid:
365                     case eStateUnloaded:
366                     case eStateAttaching:
367                     case eStateLaunching:
368                     case eStateSuspended:
369                         break;  // Ignore
370 
371                     case eStateRunning:
372                     case eStateStepping:
373                         // Still waiting to stop at entry point...
374                         break;
375 
376                     case eStateStopped:
377                     case eStateCrashed:
378                         return pid;
379 
380                     case eStateDetached:
381                     case eStateExited:
382                         if (err_str && err_len > 0)
383                             snprintf(err_str, err_len, "process exited");
384                         return INVALID_NUB_PROCESS;
385                 }
386             }
387 
388             DNBProcessResetEvents(pid, set_events);
389         }
390     }
391 
392     return INVALID_NUB_PROCESS;
393 }
394 
395 size_t
396 GetAllInfos (std::vector<struct kinfo_proc>& proc_infos)
397 {
398     size_t size = 0;
399     int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
400     u_int namelen = sizeof(name)/sizeof(int);
401     int err;
402 
403     // Try to find out how many processes are around so we can
404     // size the buffer appropriately.  sysctl's man page specifically suggests
405     // this approach, and says it returns a bit larger size than needed to
406     // handle any new processes created between then and now.
407 
408     err = ::sysctl (name, namelen, NULL, &size, NULL, 0);
409 
410     if ((err < 0) && (err != ENOMEM))
411     {
412         proc_infos.clear();
413         perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
414         return 0;
415     }
416 
417 
418     // Increase the size of the buffer by a few processes in case more have
419     // been spawned
420     proc_infos.resize (size / sizeof(struct kinfo_proc));
421     size = proc_infos.size() * sizeof(struct kinfo_proc);   // Make sure we don't exceed our resize...
422     err = ::sysctl (name, namelen, &proc_infos[0], &size, NULL, 0);
423     if (err < 0)
424     {
425         proc_infos.clear();
426         return 0;
427     }
428 
429     // Trim down our array to fit what we actually got back
430     proc_infos.resize(size / sizeof(struct kinfo_proc));
431     return proc_infos.size();
432 }
433 
434 static size_t
435 GetAllInfosMatchingName(const char *full_process_name, std::vector<struct kinfo_proc>& matching_proc_infos)
436 {
437 
438     matching_proc_infos.clear();
439     if (full_process_name && full_process_name[0])
440     {
441         // We only get the process name, not the full path, from the proc_info.  So just take the
442         // base name of the process name...
443         const char *process_name;
444         process_name = strrchr (full_process_name, '/');
445         if (process_name == NULL)
446             process_name = full_process_name;
447         else
448             process_name++;
449 
450         const int process_name_len = strlen(process_name);
451         std::vector<struct kinfo_proc> proc_infos;
452         const size_t num_proc_infos = GetAllInfos(proc_infos);
453         if (num_proc_infos > 0)
454         {
455             uint32_t i;
456             for (i=0; i<num_proc_infos; i++)
457             {
458                 // Skip zombie processes and processes with unset status
459                 if (proc_infos[i].kp_proc.p_stat == 0 || proc_infos[i].kp_proc.p_stat == SZOMB)
460                     continue;
461 
462                 // Check for process by name. We only check the first MAXCOMLEN
463                 // chars as that is all that kp_proc.p_comm holds.
464 
465                 if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm, MAXCOMLEN) == 0)
466                 {
467                     if (process_name_len > MAXCOMLEN)
468                     {
469                         // We found a matching process name whose first MAXCOMLEN
470                         // characters match, but there is more to the name than
471                         // this. We need to get the full process name.  Use proc_pidpath, which will get
472                         // us the full path to the executed process.
473 
474                         char proc_path_buf[PATH_MAX];
475 
476                         int return_val = proc_pidpath (proc_infos[i].kp_proc.p_pid, proc_path_buf, PATH_MAX);
477                         if (return_val > 0)
478                         {
479                             // Okay, now search backwards from that to see if there is a
480                             // slash in the name.  Note, even though we got all the args we don't care
481                             // because the list data is just a bunch of concatenated null terminated strings
482                             // so strrchr will start from the end of argv0.
483 
484                             const char *argv_basename = strrchr(proc_path_buf, '/');
485                             if (argv_basename)
486                             {
487                                 // Skip the '/'
488                                 ++argv_basename;
489                             }
490                             else
491                             {
492                                 // We didn't find a directory delimiter in the process argv[0], just use what was in there
493                                 argv_basename = proc_path_buf;
494                             }
495 
496                             if (argv_basename)
497                             {
498                                 if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0)
499                                 {
500                                     matching_proc_infos.push_back(proc_infos[i]);
501                                 }
502                             }
503                         }
504                     }
505                     else
506                     {
507                         // We found a matching process, add it to our list
508                         matching_proc_infos.push_back(proc_infos[i]);
509                     }
510                 }
511             }
512         }
513     }
514     // return the newly added matches.
515     return matching_proc_infos.size();
516 }
517 
518 nub_process_t
519 DNBProcessAttachWait (const char *waitfor_process_name,
520                       nub_launch_flavor_t launch_flavor,
521                       bool ignore_existing,
522                       struct timespec *timeout_abstime,
523                       useconds_t waitfor_interval,
524                       char *err_str,
525                       size_t err_len,
526                       DNBShouldCancelCallback should_cancel_callback,
527                       void *callback_data)
528 {
529     DNBError prepare_error;
530     std::vector<struct kinfo_proc> exclude_proc_infos;
531     size_t num_exclude_proc_infos;
532 
533     // If the PrepareForAttach returns a valid token, use  MachProcess to check
534     // for the process, otherwise scan the process table.
535 
536     const void *attach_token = MachProcess::PrepareForAttach (waitfor_process_name, launch_flavor, true, prepare_error);
537 
538     if (prepare_error.Fail())
539     {
540         DNBLogError ("Error in PrepareForAttach: %s", prepare_error.AsString());
541         return INVALID_NUB_PROCESS;
542     }
543 
544     if (attach_token == NULL)
545     {
546         if (ignore_existing)
547             num_exclude_proc_infos = GetAllInfosMatchingName (waitfor_process_name, exclude_proc_infos);
548         else
549             num_exclude_proc_infos = 0;
550     }
551 
552     DNBLogThreadedIf (LOG_PROCESS, "Waiting for '%s' to appear...\n", waitfor_process_name);
553 
554     // Loop and try to find the process by name
555     nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
556 
557     while (waitfor_pid == INVALID_NUB_PROCESS)
558     {
559         if (attach_token != NULL)
560         {
561             nub_process_t pid;
562             pid = MachProcess::CheckForProcess(attach_token);
563             if (pid != INVALID_NUB_PROCESS)
564             {
565                 waitfor_pid = pid;
566                 break;
567             }
568         }
569         else
570         {
571 
572             // Get the current process list, and check for matches that
573             // aren't in our original list. If anyone wants to attach
574             // to an existing process by name, they should do it with
575             // --attach=PROCNAME. Else we will wait for the first matching
576             // process that wasn't in our exclusion list.
577             std::vector<struct kinfo_proc> proc_infos;
578             const size_t num_proc_infos = GetAllInfosMatchingName (waitfor_process_name, proc_infos);
579             for (size_t i=0; i<num_proc_infos; i++)
580             {
581                 nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
582                 for (size_t j=0; j<num_exclude_proc_infos; j++)
583                 {
584                     if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid)
585                     {
586                         // This process was in our exclusion list, don't use it.
587                         curr_pid = INVALID_NUB_PROCESS;
588                         break;
589                     }
590                 }
591 
592                 // If we didn't find CURR_PID in our exclusion list, then use it.
593                 if (curr_pid != INVALID_NUB_PROCESS)
594                 {
595                     // We found our process!
596                     waitfor_pid = curr_pid;
597                     break;
598                 }
599             }
600         }
601 
602         // If we haven't found our process yet, check for a timeout
603         // and then sleep for a bit until we poll again.
604         if (waitfor_pid == INVALID_NUB_PROCESS)
605         {
606             if (timeout_abstime != NULL)
607             {
608                 // Check to see if we have a waitfor-duration option that
609                 // has timed out?
610                 if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime))
611                 {
612                     if (err_str && err_len > 0)
613                         snprintf(err_str, err_len, "operation timed out");
614                     DNBLogError ("error: waiting for process '%s' timed out.\n", waitfor_process_name);
615                     return INVALID_NUB_PROCESS;
616                 }
617             }
618 
619             // Call the should cancel callback as well...
620 
621             if (should_cancel_callback != NULL
622                 && should_cancel_callback (callback_data))
623             {
624                 DNBLogThreadedIf (LOG_PROCESS, "DNBProcessAttachWait cancelled by should_cancel callback.");
625                 waitfor_pid = INVALID_NUB_PROCESS;
626                 break;
627             }
628 
629             ::usleep (waitfor_interval);    // Sleep for WAITFOR_INTERVAL, then poll again
630         }
631     }
632 
633     if (waitfor_pid != INVALID_NUB_PROCESS)
634     {
635         DNBLogThreadedIf (LOG_PROCESS, "Attaching to %s with pid %i...\n", waitfor_process_name, waitfor_pid);
636         waitfor_pid = DNBProcessAttach (waitfor_pid, timeout_abstime, err_str, err_len);
637     }
638 
639     bool success = waitfor_pid != INVALID_NUB_PROCESS;
640     MachProcess::CleanupAfterAttach (attach_token, success, prepare_error);
641 
642     return waitfor_pid;
643 }
644 
645 nub_bool_t
646 DNBProcessDetach (nub_process_t pid)
647 {
648     MachProcessSP procSP;
649     if (GetProcessSP (pid, procSP))
650     {
651         return procSP->Detach();
652     }
653     return false;
654 }
655 
656 nub_bool_t
657 DNBProcessKill (nub_process_t pid)
658 {
659     MachProcessSP procSP;
660     if (GetProcessSP (pid, procSP))
661     {
662         return procSP->Kill ();
663     }
664     return false;
665 }
666 
667 nub_bool_t
668 DNBProcessSignal (nub_process_t pid, int signal)
669 {
670     MachProcessSP procSP;
671     if (GetProcessSP (pid, procSP))
672     {
673         return procSP->Signal (signal);
674     }
675     return false;
676 }
677 
678 
679 nub_bool_t
680 DNBProcessIsAlive (nub_process_t pid)
681 {
682     MachProcessSP procSP;
683     if (GetProcessSP (pid, procSP))
684     {
685         return MachTask::IsValid (procSP->Task().TaskPort());
686     }
687     return eStateInvalid;
688 }
689 
690 //----------------------------------------------------------------------
691 // Process and Thread state information
692 //----------------------------------------------------------------------
693 nub_state_t
694 DNBProcessGetState (nub_process_t pid)
695 {
696     MachProcessSP procSP;
697     if (GetProcessSP (pid, procSP))
698     {
699         return procSP->GetState();
700     }
701     return eStateInvalid;
702 }
703 
704 //----------------------------------------------------------------------
705 // Process and Thread state information
706 //----------------------------------------------------------------------
707 nub_bool_t
708 DNBProcessGetExitStatus (nub_process_t pid, int* status)
709 {
710     MachProcessSP procSP;
711     if (GetProcessSP (pid, procSP))
712     {
713         return procSP->GetExitStatus(status);
714     }
715     return false;
716 }
717 
718 nub_bool_t
719 DNBProcessSetExitStatus (nub_process_t pid, int status)
720 {
721     MachProcessSP procSP;
722     if (GetProcessSP (pid, procSP))
723     {
724         procSP->SetExitStatus(status);
725         return true;
726     }
727     return false;
728 }
729 
730 
731 const char *
732 DNBThreadGetName (nub_process_t pid, nub_thread_t tid)
733 {
734     MachProcessSP procSP;
735     if (GetProcessSP (pid, procSP))
736         return procSP->ThreadGetName(tid);
737     return NULL;
738 }
739 
740 
741 nub_bool_t
742 DNBThreadGetIdentifierInfo (nub_process_t pid, nub_thread_t tid, thread_identifier_info_data_t *ident_info)
743 {
744     MachProcessSP procSP;
745     if (GetProcessSP (pid, procSP))
746         return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
747     return false;
748 }
749 
750 nub_state_t
751 DNBThreadGetState (nub_process_t pid, nub_thread_t tid)
752 {
753     MachProcessSP procSP;
754     if (GetProcessSP (pid, procSP))
755     {
756         return procSP->ThreadGetState(tid);
757     }
758     return eStateInvalid;
759 }
760 
761 const char *
762 DNBStateAsString(nub_state_t state)
763 {
764     switch (state)
765     {
766     case eStateInvalid:     return "Invalid";
767     case eStateUnloaded:    return "Unloaded";
768     case eStateAttaching:   return "Attaching";
769     case eStateLaunching:   return "Launching";
770     case eStateStopped:     return "Stopped";
771     case eStateRunning:     return "Running";
772     case eStateStepping:    return "Stepping";
773     case eStateCrashed:     return "Crashed";
774     case eStateDetached:    return "Detached";
775     case eStateExited:      return "Exited";
776     case eStateSuspended:   return "Suspended";
777     }
778     return "nub_state_t ???";
779 }
780 
781 const char *
782 DNBProcessGetExecutablePath (nub_process_t pid)
783 {
784     MachProcessSP procSP;
785     if (GetProcessSP (pid, procSP))
786     {
787         return procSP->Path();
788     }
789     return NULL;
790 }
791 
792 nub_size_t
793 DNBProcessGetArgumentCount (nub_process_t pid)
794 {
795     MachProcessSP procSP;
796     if (GetProcessSP (pid, procSP))
797     {
798         return procSP->ArgumentCount();
799     }
800     return 0;
801 }
802 
803 const char *
804 DNBProcessGetArgumentAtIndex (nub_process_t pid, nub_size_t idx)
805 {
806     MachProcessSP procSP;
807     if (GetProcessSP (pid, procSP))
808     {
809         return procSP->ArgumentAtIndex (idx);
810     }
811     return NULL;
812 }
813 
814 
815 //----------------------------------------------------------------------
816 // Execution control
817 //----------------------------------------------------------------------
818 nub_bool_t
819 DNBProcessResume (nub_process_t pid, const DNBThreadResumeAction *actions, size_t num_actions)
820 {
821     DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
822     MachProcessSP procSP;
823     if (GetProcessSP (pid, procSP))
824     {
825         DNBThreadResumeActions thread_actions (actions, num_actions);
826 
827         // Below we add a default thread plan just in case one wasn't
828         // provided so all threads always know what they were supposed to do
829         if (thread_actions.IsEmpty())
830         {
831             // No thread plans were given, so the default it to run all threads
832             thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0);
833         }
834         else
835         {
836             // Some thread plans were given which means anything that wasn't
837             // specified should remain stopped.
838             thread_actions.SetDefaultThreadActionIfNeeded (eStateStopped, 0);
839         }
840         return procSP->Resume (thread_actions);
841     }
842     return false;
843 }
844 
845 nub_bool_t
846 DNBProcessHalt (nub_process_t pid)
847 {
848     DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
849     MachProcessSP procSP;
850     if (GetProcessSP (pid, procSP))
851         return procSP->Signal (SIGSTOP);
852     return false;
853 }
854 //
855 //nub_bool_t
856 //DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
857 //{
858 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)", __FUNCTION__, pid, tid, (uint32_t)step);
859 //    MachProcessSP procSP;
860 //    if (GetProcessSP (pid, procSP))
861 //    {
862 //        return procSP->Resume(tid, step, 0);
863 //    }
864 //    return false;
865 //}
866 //
867 //nub_bool_t
868 //DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t step, int signal)
869 //{
870 //    DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u, signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
871 //    MachProcessSP procSP;
872 //    if (GetProcessSP (pid, procSP))
873 //    {
874 //        return procSP->Resume(tid, step, signal);
875 //    }
876 //    return false;
877 //}
878 
879 nub_event_t
880 DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout)
881 {
882     nub_event_t result = 0;
883     MachProcessSP procSP;
884     if (GetProcessSP (pid, procSP))
885     {
886         if (wait_for_set)
887             result = procSP->Events().WaitForSetEvents(event_mask, timeout);
888         else
889             result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
890     }
891     return result;
892 }
893 
894 void
895 DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask)
896 {
897     MachProcessSP procSP;
898     if (GetProcessSP (pid, procSP))
899         procSP->Events().ResetEvents(event_mask);
900 }
901 
902 void
903 DNBProcessInterruptEvents (nub_process_t pid)
904 {
905     MachProcessSP procSP;
906     if (GetProcessSP (pid, procSP))
907         procSP->Events().SetEvents(eEventProcessAsyncInterrupt);
908 }
909 
910 
911 // Breakpoints
912 nub_break_t
913 DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware)
914 {
915     MachProcessSP procSP;
916     if (GetProcessSP (pid, procSP))
917     {
918         return procSP->CreateBreakpoint(addr, size, hardware, THREAD_NULL);
919     }
920     return INVALID_NUB_BREAK_ID;
921 }
922 
923 nub_bool_t
924 DNBBreakpointClear (nub_process_t pid, nub_break_t breakID)
925 {
926     if (NUB_BREAK_ID_IS_VALID(breakID))
927     {
928         MachProcessSP procSP;
929         if (GetProcessSP (pid, procSP))
930         {
931             return procSP->DisableBreakpoint(breakID, true);
932         }
933     }
934     return false; // Failed
935 }
936 
937 nub_ssize_t
938 DNBBreakpointGetHitCount (nub_process_t pid, nub_break_t breakID)
939 {
940     if (NUB_BREAK_ID_IS_VALID(breakID))
941     {
942         MachProcessSP procSP;
943         if (GetProcessSP (pid, procSP))
944         {
945             DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID);
946             if (bp)
947                 return bp->GetHitCount();
948         }
949     }
950     return 0;
951 }
952 
953 nub_ssize_t
954 DNBBreakpointGetIgnoreCount (nub_process_t pid, nub_break_t breakID)
955 {
956     if (NUB_BREAK_ID_IS_VALID(breakID))
957     {
958         MachProcessSP procSP;
959         if (GetProcessSP (pid, procSP))
960         {
961             DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID);
962             if (bp)
963                 return bp->GetIgnoreCount();
964         }
965     }
966     return 0;
967 }
968 
969 nub_bool_t
970 DNBBreakpointSetIgnoreCount (nub_process_t pid, nub_break_t breakID, nub_size_t ignore_count)
971 {
972     if (NUB_BREAK_ID_IS_VALID(breakID))
973     {
974         MachProcessSP procSP;
975         if (GetProcessSP (pid, procSP))
976         {
977             DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID);
978             if (bp)
979             {
980                 bp->SetIgnoreCount(ignore_count);
981                 return true;
982             }
983         }
984     }
985     return false;
986 }
987 
988 // Set the callback function for a given breakpoint. The callback function will
989 // get called as soon as the breakpoint is hit. The function will be called
990 // with the process ID, thread ID, breakpoint ID and the baton, and can return
991 //
992 nub_bool_t
993 DNBBreakpointSetCallback (nub_process_t pid, nub_break_t breakID, DNBCallbackBreakpointHit callback, void *baton)
994 {
995     if (NUB_BREAK_ID_IS_VALID(breakID))
996     {
997         MachProcessSP procSP;
998         if (GetProcessSP (pid, procSP))
999         {
1000             DNBBreakpoint *bp = procSP->Breakpoints().FindByID(breakID);
1001             if (bp)
1002             {
1003                 bp->SetCallback(callback, baton);
1004                 return true;
1005             }
1006         }
1007     }
1008     return false;
1009 }
1010 
1011 //----------------------------------------------------------------------
1012 // Dump the breakpoints stats for process PID for a breakpoint by ID.
1013 //----------------------------------------------------------------------
1014 void
1015 DNBBreakpointPrint (nub_process_t pid, nub_break_t breakID)
1016 {
1017     MachProcessSP procSP;
1018     if (GetProcessSP (pid, procSP))
1019         procSP->DumpBreakpoint(breakID);
1020 }
1021 
1022 //----------------------------------------------------------------------
1023 // Watchpoints
1024 //----------------------------------------------------------------------
1025 nub_watch_t
1026 DNBWatchpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, uint32_t watch_flags, nub_bool_t hardware)
1027 {
1028     MachProcessSP procSP;
1029     if (GetProcessSP (pid, procSP))
1030     {
1031         return procSP->CreateWatchpoint(addr, size, watch_flags, hardware, THREAD_NULL);
1032     }
1033     return INVALID_NUB_WATCH_ID;
1034 }
1035 
1036 nub_bool_t
1037 DNBWatchpointClear (nub_process_t pid, nub_watch_t watchID)
1038 {
1039     if (NUB_WATCH_ID_IS_VALID(watchID))
1040     {
1041         MachProcessSP procSP;
1042         if (GetProcessSP (pid, procSP))
1043         {
1044             return procSP->DisableWatchpoint(watchID, true);
1045         }
1046     }
1047     return false; // Failed
1048 }
1049 
1050 nub_ssize_t
1051 DNBWatchpointGetHitCount (nub_process_t pid, nub_watch_t watchID)
1052 {
1053     if (NUB_WATCH_ID_IS_VALID(watchID))
1054     {
1055         MachProcessSP procSP;
1056         if (GetProcessSP (pid, procSP))
1057         {
1058             DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID);
1059             if (bp)
1060                 return bp->GetHitCount();
1061         }
1062     }
1063     return 0;
1064 }
1065 
1066 nub_ssize_t
1067 DNBWatchpointGetIgnoreCount (nub_process_t pid, nub_watch_t watchID)
1068 {
1069     if (NUB_WATCH_ID_IS_VALID(watchID))
1070     {
1071         MachProcessSP procSP;
1072         if (GetProcessSP (pid, procSP))
1073         {
1074             DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID);
1075             if (bp)
1076                 return bp->GetIgnoreCount();
1077         }
1078     }
1079     return 0;
1080 }
1081 
1082 nub_bool_t
1083 DNBWatchpointSetIgnoreCount (nub_process_t pid, nub_watch_t watchID, nub_size_t ignore_count)
1084 {
1085     if (NUB_WATCH_ID_IS_VALID(watchID))
1086     {
1087         MachProcessSP procSP;
1088         if (GetProcessSP (pid, procSP))
1089         {
1090             DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID);
1091             if (bp)
1092             {
1093                 bp->SetIgnoreCount(ignore_count);
1094                 return true;
1095             }
1096         }
1097     }
1098     return false;
1099 }
1100 
1101 // Set the callback function for a given watchpoint. The callback function will
1102 // get called as soon as the watchpoint is hit. The function will be called
1103 // with the process ID, thread ID, watchpoint ID and the baton, and can return
1104 //
1105 nub_bool_t
1106 DNBWatchpointSetCallback (nub_process_t pid, nub_watch_t watchID, DNBCallbackBreakpointHit callback, void *baton)
1107 {
1108     if (NUB_WATCH_ID_IS_VALID(watchID))
1109     {
1110         MachProcessSP procSP;
1111         if (GetProcessSP (pid, procSP))
1112         {
1113             DNBBreakpoint *bp = procSP->Watchpoints().FindByID(watchID);
1114             if (bp)
1115             {
1116                 bp->SetCallback(callback, baton);
1117                 return true;
1118             }
1119         }
1120     }
1121     return false;
1122 }
1123 
1124 //----------------------------------------------------------------------
1125 // Dump the watchpoints stats for process PID for a watchpoint by ID.
1126 //----------------------------------------------------------------------
1127 void
1128 DNBWatchpointPrint (nub_process_t pid, nub_watch_t watchID)
1129 {
1130     MachProcessSP procSP;
1131     if (GetProcessSP (pid, procSP))
1132         procSP->DumpWatchpoint(watchID);
1133 }
1134 
1135 //----------------------------------------------------------------------
1136 // Return the number of supported hardware watchpoints.
1137 //----------------------------------------------------------------------
1138 uint32_t
1139 DNBWatchpointGetNumSupportedHWP (nub_process_t pid)
1140 {
1141     MachProcessSP procSP;
1142     if (GetProcessSP (pid, procSP))
1143         return procSP->GetNumSupportedHardwareWatchpoints();
1144     return 0;
1145 }
1146 
1147 //----------------------------------------------------------------------
1148 // Read memory in the address space of process PID. This call will take
1149 // care of setting and restoring permissions and breaking up the memory
1150 // read into multiple chunks as required.
1151 //
1152 // RETURNS: number of bytes actually read
1153 //----------------------------------------------------------------------
1154 nub_size_t
1155 DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf)
1156 {
1157     MachProcessSP procSP;
1158     if (GetProcessSP (pid, procSP))
1159         return procSP->ReadMemory(addr, size, buf);
1160     return 0;
1161 }
1162 
1163 //----------------------------------------------------------------------
1164 // Write memory to the address space of process PID. This call will take
1165 // care of setting and restoring permissions and breaking up the memory
1166 // write into multiple chunks as required.
1167 //
1168 // RETURNS: number of bytes actually written
1169 //----------------------------------------------------------------------
1170 nub_size_t
1171 DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf)
1172 {
1173     MachProcessSP procSP;
1174     if (GetProcessSP (pid, procSP))
1175         return procSP->WriteMemory(addr, size, buf);
1176     return 0;
1177 }
1178 
1179 nub_addr_t
1180 DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions)
1181 {
1182     MachProcessSP procSP;
1183     if (GetProcessSP (pid, procSP))
1184         return procSP->Task().AllocateMemory (size, permissions);
1185     return 0;
1186 }
1187 
1188 nub_bool_t
1189 DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr)
1190 {
1191     MachProcessSP procSP;
1192     if (GetProcessSP (pid, procSP))
1193         return procSP->Task().DeallocateMemory (addr);
1194     return 0;
1195 }
1196 
1197 //----------------------------------------------------------------------
1198 // Find attributes of the memory region that contains ADDR for process PID,
1199 // if possible, and return a string describing those attributes.
1200 //
1201 // Returns 1 if we could find attributes for this region and OUTBUF can
1202 // be sent to the remote debugger.
1203 //
1204 // Returns 0 if we couldn't find the attributes for a region of memory at
1205 // that address and OUTBUF should not be sent.
1206 //
1207 // Returns -1 if this platform cannot look up information about memory regions
1208 // or if we do not yet have a valid launched process.
1209 //
1210 //----------------------------------------------------------------------
1211 int
1212 DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info)
1213 {
1214     MachProcessSP procSP;
1215     if (GetProcessSP (pid, procSP))
1216         return procSP->Task().GetMemoryRegionInfo (addr, region_info);
1217 
1218     return -1;
1219 }
1220 
1221 std::string
1222 DNBProcessGetProfileData (nub_process_t pid)
1223 {
1224     MachProcessSP procSP;
1225     if (GetProcessSP (pid, procSP))
1226         return procSP->Task().GetProfileData();
1227 
1228     return std::string("");
1229 }
1230 
1231 nub_bool_t
1232 DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec)
1233 {
1234     MachProcessSP procSP;
1235     if (GetProcessSP (pid, procSP))
1236     {
1237         procSP->SetEnableAsyncProfiling(enable, interval_usec);
1238         return true;
1239     }
1240 
1241     return false;
1242 }
1243 
1244 //----------------------------------------------------------------------
1245 // Formatted output that uses memory and registers from process and
1246 // thread in place of arguments.
1247 //----------------------------------------------------------------------
1248 nub_size_t
1249 DNBPrintf (nub_process_t pid, nub_thread_t tid, nub_addr_t base_addr, FILE *file, const char *format)
1250 {
1251     if (file == NULL)
1252         return 0;
1253     enum printf_flags
1254     {
1255         alternate_form          = (1 << 0),
1256         zero_padding            = (1 << 1),
1257         negative_field_width    = (1 << 2),
1258         blank_space             = (1 << 3),
1259         show_sign               = (1 << 4),
1260         show_thousands_separator= (1 << 5),
1261     };
1262 
1263     enum printf_length_modifiers
1264     {
1265         length_mod_h            = (1 << 0),
1266         length_mod_hh           = (1 << 1),
1267         length_mod_l            = (1 << 2),
1268         length_mod_ll           = (1 << 3),
1269         length_mod_L            = (1 << 4),
1270         length_mod_j            = (1 << 5),
1271         length_mod_t            = (1 << 6),
1272         length_mod_z            = (1 << 7),
1273         length_mod_q            = (1 << 8),
1274     };
1275 
1276     nub_addr_t addr = base_addr;
1277     char *end_format = (char*)format + strlen(format);
1278     char *end = NULL;    // For strtoXXXX calls;
1279     std::basic_string<uint8_t> buf;
1280     nub_size_t total_bytes_read = 0;
1281     DNBDataRef data;
1282     const char *f;
1283     for (f = format; *f != '\0' && f < end_format; f++)
1284     {
1285         char ch = *f;
1286         switch (ch)
1287         {
1288         case '%':
1289             {
1290                 f++;    // Skip the '%' character
1291 //                int min_field_width = 0;
1292 //                int precision = 0;
1293                 //uint32_t flags = 0;
1294                 uint32_t length_modifiers = 0;
1295                 uint32_t byte_size = 0;
1296                 uint32_t actual_byte_size = 0;
1297                 bool is_string = false;
1298                 bool is_register = false;
1299                 DNBRegisterValue register_value;
1300                 int64_t    register_offset = 0;
1301                 nub_addr_t register_addr = INVALID_NUB_ADDRESS;
1302 
1303                 // Create the format string to use for this conversion specification
1304                 // so we can remove and mprintf specific flags and formatters.
1305                 std::string fprintf_format("%");
1306 
1307                 // Decode any flags
1308                 switch (*f)
1309                 {
1310                 case '#': fprintf_format += *f++; break; //flags |= alternate_form;          break;
1311                 case '0': fprintf_format += *f++; break; //flags |= zero_padding;            break;
1312                 case '-': fprintf_format += *f++; break; //flags |= negative_field_width;    break;
1313                 case ' ': fprintf_format += *f++; break; //flags |= blank_space;             break;
1314                 case '+': fprintf_format += *f++; break; //flags |= show_sign;               break;
1315                 case ',': fprintf_format += *f++; break; //flags |= show_thousands_separator;break;
1316                 case '{':
1317                 case '[':
1318                     {
1319                         // We have a register name specification that can take two forms:
1320                         // ${regname} or ${regname+offset}
1321                         //        The action is to read the register value and add the signed offset
1322                         //        (if any) and use that as the value to format.
1323                         // $[regname] or $[regname+offset]
1324                         //        The action is to read the register value and add the signed offset
1325                         //        (if any) and use the result as an address to dereference. The size
1326                         //        of what is dereferenced is specified by the actual byte size that
1327                         //        follows the minimum field width and precision (see comments below).
1328                         switch (*f)
1329                         {
1330                         case '{':
1331                         case '[':
1332                             {
1333                                 char open_scope_ch = *f;
1334                                 f++;
1335                                 const char *reg_name = f;
1336                                 size_t reg_name_length = strcspn(f, "+-}]");
1337                                 if (reg_name_length > 0)
1338                                 {
1339                                     std::string register_name(reg_name, reg_name_length);
1340                                     f += reg_name_length;
1341                                     register_offset = strtoll(f, &end, 0);
1342                                     if (f < end)
1343                                         f = end;
1344                                     if ((open_scope_ch == '{' && *f != '}') || (open_scope_ch == '[' && *f != ']'))
1345                                     {
1346                                         fprintf(file, "error: Invalid register format string. Valid formats are %%{regname} or %%{regname+offset}, %%[regname] or %%[regname+offset]\n");
1347                                         return total_bytes_read;
1348                                     }
1349                                     else
1350                                     {
1351                                         f++;
1352                                         if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, register_name.c_str(), &register_value))
1353                                         {
1354                                             // Set the address to dereference using the register value plus the offset
1355                                             switch (register_value.info.size)
1356                                             {
1357                                             default:
1358                                             case 0:
1359                                                 fprintf (file, "error: unsupported register size of %u.\n", register_value.info.size);
1360                                                 return total_bytes_read;
1361 
1362                                             case 1:        register_addr = register_value.value.uint8  + register_offset; break;
1363                                             case 2:        register_addr = register_value.value.uint16 + register_offset; break;
1364                                             case 4:        register_addr = register_value.value.uint32 + register_offset; break;
1365                                             case 8:        register_addr = register_value.value.uint64 + register_offset; break;
1366                                             case 16:
1367                                                 if (open_scope_ch == '[')
1368                                                 {
1369                                                     fprintf (file, "error: register size (%u) too large for address.\n", register_value.info.size);
1370                                                     return total_bytes_read;
1371                                                 }
1372                                                 break;
1373                                             }
1374 
1375                                             if (open_scope_ch == '{')
1376                                             {
1377                                                 byte_size = register_value.info.size;
1378                                                 is_register = true;    // value is in a register
1379 
1380                                             }
1381                                             else
1382                                             {
1383                                                 addr = register_addr;    // Use register value and offset as the address
1384                                             }
1385                                         }
1386                                         else
1387                                         {
1388                                             fprintf(file, "error: unable to read register '%s' for process %#.4x and thread %#.4x\n", register_name.c_str(), pid, tid);
1389                                             return total_bytes_read;
1390                                         }
1391                                     }
1392                                 }
1393                             }
1394                             break;
1395 
1396                         default:
1397                             fprintf(file, "error: %%$ must be followed by (regname + n) or [regname + n]\n");
1398                             return total_bytes_read;
1399                         }
1400                     }
1401                     break;
1402                 }
1403 
1404                 // Check for a minimum field width
1405                 if (isdigit(*f))
1406                 {
1407                     //min_field_width = strtoul(f, &end, 10);
1408                     strtoul(f, &end, 10);
1409                     if (end > f)
1410                     {
1411                         fprintf_format.append(f, end - f);
1412                         f = end;
1413                     }
1414                 }
1415 
1416 
1417                 // Check for a precision
1418                 if (*f == '.')
1419                 {
1420                     f++;
1421                     if (isdigit(*f))
1422                     {
1423                         fprintf_format += '.';
1424                         //precision = strtoul(f, &end, 10);
1425                         strtoul(f, &end, 10);
1426                         if (end > f)
1427                         {
1428                             fprintf_format.append(f, end - f);
1429                             f = end;
1430                         }
1431                     }
1432                 }
1433 
1434 
1435                 // mprintf specific: read the optional actual byte size (abs)
1436                 // after the standard minimum field width (mfw) and precision (prec).
1437                 // Standard printf calls you can have "mfw.prec" or ".prec", but
1438                 // mprintf can have "mfw.prec.abs", ".prec.abs" or "..abs". This is nice
1439                 // for strings that may be in a fixed size buffer, but may not use all bytes
1440                 // in that buffer for printable characters.
1441                 if (*f == '.')
1442                 {
1443                     f++;
1444                     actual_byte_size = strtoul(f, &end, 10);
1445                     if (end > f)
1446                     {
1447                         byte_size = actual_byte_size;
1448                         f = end;
1449                     }
1450                 }
1451 
1452                 // Decode the length modifiers
1453                 switch (*f)
1454                 {
1455                 case 'h':    // h and hh length modifiers
1456                     fprintf_format += *f++;
1457                     length_modifiers |= length_mod_h;
1458                     if (*f == 'h')
1459                     {
1460                         fprintf_format += *f++;
1461                         length_modifiers |= length_mod_hh;
1462                     }
1463                     break;
1464 
1465                 case 'l': // l and ll length modifiers
1466                     fprintf_format += *f++;
1467                     length_modifiers |= length_mod_l;
1468                     if (*f == 'h')
1469                     {
1470                         fprintf_format += *f++;
1471                         length_modifiers |= length_mod_ll;
1472                     }
1473                     break;
1474 
1475                 case 'L':    fprintf_format += *f++;    length_modifiers |= length_mod_L;    break;
1476                 case 'j':    fprintf_format += *f++;    length_modifiers |= length_mod_j;    break;
1477                 case 't':    fprintf_format += *f++;    length_modifiers |= length_mod_t;    break;
1478                 case 'z':    fprintf_format += *f++;    length_modifiers |= length_mod_z;    break;
1479                 case 'q':    fprintf_format += *f++;    length_modifiers |= length_mod_q;    break;
1480                 }
1481 
1482                 // Decode the conversion specifier
1483                 switch (*f)
1484                 {
1485                 case '_':
1486                     // mprintf specific format items
1487                     {
1488                         ++f;    // Skip the '_' character
1489                         switch (*f)
1490                         {
1491                         case 'a':    // Print the current address
1492                             ++f;
1493                             fprintf_format += "ll";
1494                             fprintf_format += *f;    // actual format to show address with folows the 'a' ("%_ax")
1495                             fprintf (file, fprintf_format.c_str(), addr);
1496                             break;
1497                         case 'o':    // offset from base address
1498                             ++f;
1499                             fprintf_format += "ll";
1500                             fprintf_format += *f;    // actual format to show address with folows the 'a' ("%_ox")
1501                             fprintf(file, fprintf_format.c_str(), addr - base_addr);
1502                             break;
1503                         default:
1504                             fprintf (file, "error: unsupported mprintf specific format character '%c'.\n", *f);
1505                             break;
1506                         }
1507                         continue;
1508                     }
1509                     break;
1510 
1511                 case 'D':
1512                 case 'O':
1513                 case 'U':
1514                     fprintf_format += *f;
1515                     if (byte_size == 0)
1516                         byte_size = sizeof(long int);
1517                     break;
1518 
1519                 case 'd':
1520                 case 'i':
1521                 case 'o':
1522                 case 'u':
1523                 case 'x':
1524                 case 'X':
1525                     fprintf_format += *f;
1526                     if (byte_size == 0)
1527                     {
1528                         if (length_modifiers & length_mod_hh)
1529                             byte_size = sizeof(char);
1530                         else if (length_modifiers & length_mod_h)
1531                             byte_size = sizeof(short);
1532                         else if (length_modifiers & length_mod_ll)
1533                             byte_size = sizeof(long long);
1534                         else if (length_modifiers & length_mod_l)
1535                             byte_size = sizeof(long);
1536                         else
1537                             byte_size = sizeof(int);
1538                     }
1539                     break;
1540 
1541                 case 'a':
1542                 case 'A':
1543                 case 'f':
1544                 case 'F':
1545                 case 'e':
1546                 case 'E':
1547                 case 'g':
1548                 case 'G':
1549                     fprintf_format += *f;
1550                     if (byte_size == 0)
1551                     {
1552                         if (length_modifiers & length_mod_L)
1553                             byte_size = sizeof(long double);
1554                         else
1555                             byte_size = sizeof(double);
1556                     }
1557                     break;
1558 
1559                 case 'c':
1560                     if ((length_modifiers & length_mod_l) == 0)
1561                     {
1562                         fprintf_format += *f;
1563                         if (byte_size == 0)
1564                             byte_size = sizeof(char);
1565                         break;
1566                     }
1567                     // Fall through to 'C' modifier below...
1568 
1569                 case 'C':
1570                     fprintf_format += *f;
1571                     if (byte_size == 0)
1572                         byte_size = sizeof(wchar_t);
1573                     break;
1574 
1575                 case 's':
1576                     fprintf_format += *f;
1577                     if (is_register || byte_size == 0)
1578                         is_string = 1;
1579                     break;
1580 
1581                 case 'p':
1582                     fprintf_format += *f;
1583                     if (byte_size == 0)
1584                         byte_size = sizeof(void*);
1585                     break;
1586                 }
1587 
1588                 if (is_string)
1589                 {
1590                     std::string mem_string;
1591                     const size_t string_buf_len = 4;
1592                     char string_buf[string_buf_len+1];
1593                     char *string_buf_end = string_buf + string_buf_len;
1594                     string_buf[string_buf_len] = '\0';
1595                     nub_size_t bytes_read;
1596                     nub_addr_t str_addr = is_register ? register_addr : addr;
1597                     while ((bytes_read = DNBProcessMemoryRead(pid, str_addr, string_buf_len, &string_buf[0])) > 0)
1598                     {
1599                         // Did we get a NULL termination character yet?
1600                         if (strchr(string_buf, '\0') == string_buf_end)
1601                         {
1602                             // no NULL terminator yet, append as a std::string
1603                             mem_string.append(string_buf, string_buf_len);
1604                             str_addr += string_buf_len;
1605                         }
1606                         else
1607                         {
1608                             // yep
1609                             break;
1610                         }
1611                     }
1612                     // Append as a C-string so we don't get the extra NULL
1613                     // characters in the temp buffer (since it was resized)
1614                     mem_string += string_buf;
1615                     size_t mem_string_len = mem_string.size() + 1;
1616                     fprintf(file, fprintf_format.c_str(), mem_string.c_str());
1617                     if (mem_string_len > 0)
1618                     {
1619                         if (!is_register)
1620                         {
1621                             addr += mem_string_len;
1622                             total_bytes_read += mem_string_len;
1623                         }
1624                     }
1625                     else
1626                         return total_bytes_read;
1627                 }
1628                 else
1629                 if (byte_size > 0)
1630                 {
1631                     buf.resize(byte_size);
1632                     nub_size_t bytes_read = 0;
1633                     if (is_register)
1634                         bytes_read = register_value.info.size;
1635                     else
1636                         bytes_read = DNBProcessMemoryRead(pid, addr, buf.size(), &buf[0]);
1637                     if (bytes_read > 0)
1638                     {
1639                         if (!is_register)
1640                             total_bytes_read += bytes_read;
1641 
1642                         if (bytes_read == byte_size)
1643                         {
1644                             switch (*f)
1645                             {
1646                             case 'd':
1647                             case 'i':
1648                             case 'o':
1649                             case 'u':
1650                             case 'X':
1651                             case 'x':
1652                             case 'a':
1653                             case 'A':
1654                             case 'f':
1655                             case 'F':
1656                             case 'e':
1657                             case 'E':
1658                             case 'g':
1659                             case 'G':
1660                             case 'p':
1661                             case 'c':
1662                             case 'C':
1663                                 {
1664                                     if (is_register)
1665                                         data.SetData(&register_value.value.v_uint8[0], register_value.info.size);
1666                                     else
1667                                         data.SetData(&buf[0], bytes_read);
1668                                     DNBDataRef::offset_t data_offset = 0;
1669                                     if (byte_size <= 4)
1670                                     {
1671                                         uint32_t u32 = data.GetMax32(&data_offset, byte_size);
1672                                         // Show the actual byte width when displaying hex
1673                                         fprintf(file, fprintf_format.c_str(), u32);
1674                                     }
1675                                     else if (byte_size <= 8)
1676                                     {
1677                                         uint64_t u64 = data.GetMax64(&data_offset, byte_size);
1678                                         // Show the actual byte width when displaying hex
1679                                         fprintf(file, fprintf_format.c_str(), u64);
1680                                     }
1681                                     else
1682                                     {
1683                                         fprintf(file, "error: integer size not supported, must be 8 bytes or less (%u bytes).\n", byte_size);
1684                                     }
1685                                     if (!is_register)
1686                                         addr += byte_size;
1687                                 }
1688                                 break;
1689 
1690                             case 's':
1691                                 fprintf(file, fprintf_format.c_str(), buf.c_str());
1692                                 addr += byte_size;
1693                                 break;
1694 
1695                             default:
1696                                 fprintf(file, "error: unsupported conversion specifier '%c'.\n", *f);
1697                                 break;
1698                             }
1699                         }
1700                     }
1701                 }
1702                 else
1703                     return total_bytes_read;
1704             }
1705             break;
1706 
1707         case '\\':
1708             {
1709                 f++;
1710                 switch (*f)
1711                 {
1712                 case 'e': ch = '\e'; break;
1713                 case 'a': ch = '\a'; break;
1714                 case 'b': ch = '\b'; break;
1715                 case 'f': ch = '\f'; break;
1716                 case 'n': ch = '\n'; break;
1717                 case 'r': ch = '\r'; break;
1718                 case 't': ch = '\t'; break;
1719                 case 'v': ch = '\v'; break;
1720                 case '\'': ch = '\''; break;
1721                 case '\\': ch = '\\'; break;
1722                 case '0':
1723                 case '1':
1724                 case '2':
1725                 case '3':
1726                 case '4':
1727                 case '5':
1728                 case '6':
1729                 case '7':
1730                     ch = strtoul(f, &end, 8);
1731                     f = end;
1732                     break;
1733                 default:
1734                     ch = *f;
1735                     break;
1736                 }
1737                 fputc(ch, file);
1738             }
1739             break;
1740 
1741         default:
1742             fputc(ch, file);
1743             break;
1744         }
1745     }
1746     return total_bytes_read;
1747 }
1748 
1749 
1750 //----------------------------------------------------------------------
1751 // Get the number of threads for the specified process.
1752 //----------------------------------------------------------------------
1753 nub_size_t
1754 DNBProcessGetNumThreads (nub_process_t pid)
1755 {
1756     MachProcessSP procSP;
1757     if (GetProcessSP (pid, procSP))
1758         return procSP->GetNumThreads();
1759     return 0;
1760 }
1761 
1762 //----------------------------------------------------------------------
1763 // Get the thread ID of the current thread.
1764 //----------------------------------------------------------------------
1765 nub_thread_t
1766 DNBProcessGetCurrentThread (nub_process_t pid)
1767 {
1768     MachProcessSP procSP;
1769     if (GetProcessSP (pid, procSP))
1770         return procSP->GetCurrentThread();
1771     return 0;
1772 }
1773 
1774 //----------------------------------------------------------------------
1775 // Change the current thread.
1776 //----------------------------------------------------------------------
1777 nub_thread_t
1778 DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid)
1779 {
1780     MachProcessSP procSP;
1781     if (GetProcessSP (pid, procSP))
1782         return procSP->SetCurrentThread (tid);
1783     return INVALID_NUB_THREAD;
1784 }
1785 
1786 
1787 //----------------------------------------------------------------------
1788 // Dump a string describing a thread's stop reason to the specified file
1789 // handle
1790 //----------------------------------------------------------------------
1791 nub_bool_t
1792 DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, struct DNBThreadStopInfo *stop_info)
1793 {
1794     MachProcessSP procSP;
1795     if (GetProcessSP (pid, procSP))
1796         return procSP->GetThreadStoppedReason (tid, stop_info);
1797     return false;
1798 }
1799 
1800 //----------------------------------------------------------------------
1801 // Return string description for the specified thread.
1802 //
1803 // RETURNS: NULL if the thread isn't valid, else a NULL terminated C
1804 // string from a static buffer that must be copied prior to subsequent
1805 // calls.
1806 //----------------------------------------------------------------------
1807 const char *
1808 DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid)
1809 {
1810     MachProcessSP procSP;
1811     if (GetProcessSP (pid, procSP))
1812         return procSP->GetThreadInfo (tid);
1813     return NULL;
1814 }
1815 
1816 //----------------------------------------------------------------------
1817 // Get the thread ID given a thread index.
1818 //----------------------------------------------------------------------
1819 nub_thread_t
1820 DNBProcessGetThreadAtIndex (nub_process_t pid, size_t thread_idx)
1821 {
1822     MachProcessSP procSP;
1823     if (GetProcessSP (pid, procSP))
1824         return procSP->GetThreadAtIndex (thread_idx);
1825     return INVALID_NUB_THREAD;
1826 }
1827 
1828 //----------------------------------------------------------------------
1829 // Do whatever is needed to sync the thread's register state with it's kernel values.
1830 //----------------------------------------------------------------------
1831 nub_bool_t
1832 DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid)
1833 {
1834     MachProcessSP procSP;
1835     if (GetProcessSP (pid, procSP))
1836         return procSP->SyncThreadState (tid);
1837     return false;
1838 
1839 }
1840 
1841 nub_addr_t
1842 DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid)
1843 {
1844     MachProcessSP procSP;
1845     DNBError err;
1846     if (GetProcessSP (pid, procSP))
1847         return procSP->Task().GetDYLDAllImageInfosAddress (err);
1848     return INVALID_NUB_ADDRESS;
1849 }
1850 
1851 
1852 nub_bool_t
1853 DNBProcessSharedLibrariesUpdated(nub_process_t pid)
1854 {
1855     MachProcessSP procSP;
1856     if (GetProcessSP (pid, procSP))
1857     {
1858         procSP->SharedLibrariesUpdated ();
1859         return true;
1860     }
1861     return false;
1862 }
1863 
1864 //----------------------------------------------------------------------
1865 // Get the current shared library information for a process. Only return
1866 // the shared libraries that have changed since the last shared library
1867 // state changed event if only_changed is non-zero.
1868 //----------------------------------------------------------------------
1869 nub_size_t
1870 DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, struct DNBExecutableImageInfo **image_infos)
1871 {
1872     MachProcessSP procSP;
1873     if (GetProcessSP (pid, procSP))
1874         return procSP->CopyImageInfos (image_infos, only_changed);
1875 
1876     // If we have no process, then return NULL for the shared library info
1877     // and zero for shared library count
1878     *image_infos = NULL;
1879     return 0;
1880 }
1881 
1882 //----------------------------------------------------------------------
1883 // Get the register set information for a specific thread.
1884 //----------------------------------------------------------------------
1885 const DNBRegisterSetInfo *
1886 DNBGetRegisterSetInfo (nub_size_t *num_reg_sets)
1887 {
1888     return DNBArchProtocol::GetRegisterSetInfo (num_reg_sets);
1889 }
1890 
1891 
1892 //----------------------------------------------------------------------
1893 // Read a register value by register set and register index.
1894 //----------------------------------------------------------------------
1895 nub_bool_t
1896 DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value)
1897 {
1898     MachProcessSP procSP;
1899     ::bzero (value, sizeof(DNBRegisterValue));
1900     if (GetProcessSP (pid, procSP))
1901     {
1902         if (tid != INVALID_NUB_THREAD)
1903             return procSP->GetRegisterValue (tid, set, reg, value);
1904     }
1905     return false;
1906 }
1907 
1908 nub_bool_t
1909 DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value)
1910 {
1911     if (tid != INVALID_NUB_THREAD)
1912     {
1913         MachProcessSP procSP;
1914         if (GetProcessSP (pid, procSP))
1915             return procSP->SetRegisterValue (tid, set, reg, value);
1916     }
1917     return false;
1918 }
1919 
1920 nub_size_t
1921 DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len)
1922 {
1923     MachProcessSP procSP;
1924     if (GetProcessSP (pid, procSP))
1925     {
1926         if (tid != INVALID_NUB_THREAD)
1927             return procSP->GetThreadList().GetRegisterContext (tid, buf, buf_len);
1928     }
1929     ::bzero (buf, buf_len);
1930     return 0;
1931 
1932 }
1933 
1934 nub_size_t
1935 DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len)
1936 {
1937     MachProcessSP procSP;
1938     if (GetProcessSP (pid, procSP))
1939     {
1940         if (tid != INVALID_NUB_THREAD)
1941             return procSP->GetThreadList().SetRegisterContext (tid, buf, buf_len);
1942     }
1943     return 0;
1944 }
1945 
1946 //----------------------------------------------------------------------
1947 // Read a register value by name.
1948 //----------------------------------------------------------------------
1949 nub_bool_t
1950 DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t reg_set, const char *reg_name, DNBRegisterValue *value)
1951 {
1952     MachProcessSP procSP;
1953     ::bzero (value, sizeof(DNBRegisterValue));
1954     if (GetProcessSP (pid, procSP))
1955     {
1956         const struct DNBRegisterSetInfo *set_info;
1957         nub_size_t num_reg_sets = 0;
1958         set_info = DNBGetRegisterSetInfo (&num_reg_sets);
1959         if (set_info)
1960         {
1961             uint32_t set = reg_set;
1962             uint32_t reg;
1963             if (set == REGISTER_SET_ALL)
1964             {
1965                 for (set = 1; set < num_reg_sets; ++set)
1966                 {
1967                     for (reg = 0; reg < set_info[set].num_registers; ++reg)
1968                     {
1969                         if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1970                             return procSP->GetRegisterValue (tid, set, reg, value);
1971                     }
1972                 }
1973             }
1974             else
1975             {
1976                 for (reg = 0; reg < set_info[set].num_registers; ++reg)
1977                 {
1978                     if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1979                         return procSP->GetRegisterValue (tid, set, reg, value);
1980                 }
1981             }
1982         }
1983     }
1984     return false;
1985 }
1986 
1987 
1988 //----------------------------------------------------------------------
1989 // Read a register set and register number from the register name.
1990 //----------------------------------------------------------------------
1991 nub_bool_t
1992 DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info)
1993 {
1994     const struct DNBRegisterSetInfo *set_info;
1995     nub_size_t num_reg_sets = 0;
1996     set_info = DNBGetRegisterSetInfo (&num_reg_sets);
1997     if (set_info)
1998     {
1999         uint32_t set, reg;
2000         for (set = 1; set < num_reg_sets; ++set)
2001         {
2002             for (reg = 0; reg < set_info[set].num_registers; ++reg)
2003             {
2004                 if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
2005                 {
2006                     *info = set_info[set].registers[reg];
2007                     return true;
2008                 }
2009             }
2010         }
2011 
2012         for (set = 1; set < num_reg_sets; ++set)
2013         {
2014             uint32_t reg;
2015             for (reg = 0; reg < set_info[set].num_registers; ++reg)
2016             {
2017                 if (set_info[set].registers[reg].alt == NULL)
2018                     continue;
2019 
2020                 if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0)
2021                 {
2022                     *info = set_info[set].registers[reg];
2023                     return true;
2024                 }
2025             }
2026         }
2027     }
2028 
2029     ::bzero (info, sizeof(DNBRegisterInfo));
2030     return false;
2031 }
2032 
2033 
2034 //----------------------------------------------------------------------
2035 // Set the name to address callback function that this nub can use
2036 // for any name to address lookups that are needed.
2037 //----------------------------------------------------------------------
2038 nub_bool_t
2039 DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton)
2040 {
2041     MachProcessSP procSP;
2042     if (GetProcessSP (pid, procSP))
2043     {
2044         procSP->SetNameToAddressCallback (callback, baton);
2045         return true;
2046     }
2047     return false;
2048 }
2049 
2050 
2051 //----------------------------------------------------------------------
2052 // Set the name to address callback function that this nub can use
2053 // for any name to address lookups that are needed.
2054 //----------------------------------------------------------------------
2055 nub_bool_t
2056 DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void  *baton)
2057 {
2058     MachProcessSP procSP;
2059     if (GetProcessSP (pid, procSP))
2060     {
2061         procSP->SetSharedLibraryInfoCallback (callback, baton);
2062         return true;
2063     }
2064     return false;
2065 }
2066 
2067 nub_addr_t
2068 DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib)
2069 {
2070     MachProcessSP procSP;
2071     if (GetProcessSP (pid, procSP))
2072     {
2073         return procSP->LookupSymbol (name, shlib);
2074     }
2075     return INVALID_NUB_ADDRESS;
2076 }
2077 
2078 
2079 nub_size_t
2080 DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size)
2081 {
2082     MachProcessSP procSP;
2083     if (GetProcessSP (pid, procSP))
2084         return procSP->GetAvailableSTDOUT (buf, buf_size);
2085     return 0;
2086 }
2087 
2088 nub_size_t
2089 DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size)
2090 {
2091     MachProcessSP procSP;
2092     if (GetProcessSP (pid, procSP))
2093         return procSP->GetAvailableSTDERR (buf, buf_size);
2094     return 0;
2095 }
2096 
2097 nub_size_t
2098 DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size)
2099 {
2100     MachProcessSP procSP;
2101     if (GetProcessSP (pid, procSP))
2102         return procSP->GetAsyncProfileData (buf, buf_size);
2103     return 0;
2104 }
2105 
2106 nub_size_t
2107 DNBProcessGetStopCount (nub_process_t pid)
2108 {
2109     MachProcessSP procSP;
2110     if (GetProcessSP (pid, procSP))
2111         return procSP->StopCount();
2112     return 0;
2113 }
2114 
2115 uint32_t
2116 DNBProcessGetCPUType (nub_process_t pid)
2117 {
2118     MachProcessSP procSP;
2119     if (GetProcessSP (pid, procSP))
2120         return procSP->GetCPUType ();
2121     return 0;
2122 
2123 }
2124 
2125 nub_bool_t
2126 DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size)
2127 {
2128     if (path == NULL || path[0] == '\0')
2129         return false;
2130 
2131     char max_path[PATH_MAX];
2132     std::string result;
2133     CFString::GlobPath(path, result);
2134 
2135     if (result.empty())
2136         result = path;
2137 
2138     struct stat path_stat;
2139     if (::stat(path, &path_stat) == 0)
2140     {
2141         if ((path_stat.st_mode & S_IFMT) == S_IFDIR)
2142         {
2143             CFBundle bundle (path);
2144             CFReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
2145             if (url.get())
2146             {
2147                 if (::CFURLGetFileSystemRepresentation (url.get(), true, (UInt8*)resolved_path, resolved_path_size))
2148                     return true;
2149             }
2150         }
2151     }
2152 
2153     if (realpath(path, max_path))
2154     {
2155         // Found the path relatively...
2156         ::strncpy(resolved_path, max_path, resolved_path_size);
2157         return strlen(resolved_path) + 1 < resolved_path_size;
2158     }
2159     else
2160     {
2161         // Not a relative path, check the PATH environment variable if the
2162         const char *PATH = getenv("PATH");
2163         if (PATH)
2164         {
2165             const char *curr_path_start = PATH;
2166             const char *curr_path_end;
2167             while (curr_path_start && *curr_path_start)
2168             {
2169                 curr_path_end = strchr(curr_path_start, ':');
2170                 if (curr_path_end == NULL)
2171                 {
2172                     result.assign(curr_path_start);
2173                     curr_path_start = NULL;
2174                 }
2175                 else if (curr_path_end > curr_path_start)
2176                 {
2177                     size_t len = curr_path_end - curr_path_start;
2178                     result.assign(curr_path_start, len);
2179                     curr_path_start += len + 1;
2180                 }
2181                 else
2182                     break;
2183 
2184                 result += '/';
2185                 result += path;
2186                 struct stat s;
2187                 if (stat(result.c_str(), &s) == 0)
2188                 {
2189                     ::strncpy(resolved_path, result.c_str(), resolved_path_size);
2190                     return result.size() + 1 < resolved_path_size;
2191                 }
2192             }
2193         }
2194     }
2195     return false;
2196 }
2197 
2198 
2199 void
2200 DNBInitialize()
2201 {
2202     DNBLogThreadedIf (LOG_PROCESS, "DNBInitialize ()");
2203 #if defined (__i386__) || defined (__x86_64__)
2204     DNBArchImplI386::Initialize();
2205     DNBArchImplX86_64::Initialize();
2206 #elif defined (__arm__)
2207     DNBArchMachARM::Initialize();
2208 #endif
2209 }
2210 
2211 void
2212 DNBTerminate()
2213 {
2214 }
2215 
2216 nub_bool_t
2217 DNBSetArchitecture (const char *arch)
2218 {
2219     if (arch && arch[0])
2220     {
2221         if (strcasecmp (arch, "i386") == 0)
2222             return DNBArchProtocol::SetArchitecture (CPU_TYPE_I386);
2223         else if (strcasecmp (arch, "x86_64") == 0)
2224             return DNBArchProtocol::SetArchitecture (CPU_TYPE_X86_64);
2225         else if (strstr (arch, "arm") == arch)
2226             return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM);
2227     }
2228     return false;
2229 }
2230