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