DNB.cpp revision 8492942e52539a5c4fb4c06a865ad8479d496340
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                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
284nub_process_t
285DNBProcessAttachByName (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
308nub_process_t
309DNBProcessAttach (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
395size_t
396GetAllInfos (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
434static size_t
435GetAllInfosMatchingName(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
518nub_process_t
519DNBProcessAttachWait (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
645nub_bool_t
646DNBProcessDetach (nub_process_t pid)
647{
648    MachProcessSP procSP;
649    if (GetProcessSP (pid, procSP))
650    {
651        return procSP->Detach();
652    }
653    return false;
654}
655
656nub_bool_t
657DNBProcessKill (nub_process_t pid)
658{
659    MachProcessSP procSP;
660    if (GetProcessSP (pid, procSP))
661    {
662        return procSP->Kill ();
663    }
664    return false;
665}
666
667nub_bool_t
668DNBProcessSignal (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
679nub_bool_t
680DNBProcessIsAlive (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//----------------------------------------------------------------------
693nub_state_t
694DNBProcessGetState (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//----------------------------------------------------------------------
707nub_bool_t
708DNBProcessGetExitStatus (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
718nub_bool_t
719DNBProcessSetExitStatus (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
731const char *
732DNBThreadGetName (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
741nub_bool_t
742DNBThreadGetIdentifierInfo (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
750nub_state_t
751DNBThreadGetState (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
761const char *
762DNBStateAsString(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
781const char *
782DNBProcessGetExecutablePath (nub_process_t pid)
783{
784    MachProcessSP procSP;
785    if (GetProcessSP (pid, procSP))
786    {
787        return procSP->Path();
788    }
789    return NULL;
790}
791
792nub_size_t
793DNBProcessGetArgumentCount (nub_process_t pid)
794{
795    MachProcessSP procSP;
796    if (GetProcessSP (pid, procSP))
797    {
798        return procSP->ArgumentCount();
799    }
800    return 0;
801}
802
803const char *
804DNBProcessGetArgumentAtIndex (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//----------------------------------------------------------------------
818nub_bool_t
819DNBProcessResume (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
845nub_bool_t
846DNBProcessHalt (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
879nub_event_t
880DNBProcessWaitForEvents (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
894void
895DNBProcessResetEvents (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
902void
903DNBProcessInterruptEvents (nub_process_t pid)
904{
905    MachProcessSP procSP;
906    if (GetProcessSP (pid, procSP))
907        procSP->Events().SetEvents(eEventProcessAsyncInterrupt);
908}
909
910
911// Breakpoints
912nub_break_t
913DNBBreakpointSet (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
923nub_bool_t
924DNBBreakpointClear (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
937nub_ssize_t
938DNBBreakpointGetHitCount (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
953nub_ssize_t
954DNBBreakpointGetIgnoreCount (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
969nub_bool_t
970DNBBreakpointSetIgnoreCount (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//
992nub_bool_t
993DNBBreakpointSetCallback (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//----------------------------------------------------------------------
1014void
1015DNBBreakpointPrint (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//----------------------------------------------------------------------
1025nub_watch_t
1026DNBWatchpointSet (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
1036nub_bool_t
1037DNBWatchpointClear (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
1050nub_ssize_t
1051DNBWatchpointGetHitCount (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
1066nub_ssize_t
1067DNBWatchpointGetIgnoreCount (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
1082nub_bool_t
1083DNBWatchpointSetIgnoreCount (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//
1105nub_bool_t
1106DNBWatchpointSetCallback (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//----------------------------------------------------------------------
1127void
1128DNBWatchpointPrint (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//----------------------------------------------------------------------
1138uint32_t
1139DNBWatchpointGetNumSupportedHWP (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//----------------------------------------------------------------------
1154nub_size_t
1155DNBProcessMemoryRead (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//----------------------------------------------------------------------
1170nub_size_t
1171DNBProcessMemoryWrite (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
1179nub_addr_t
1180DNBProcessMemoryAllocate (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
1188nub_bool_t
1189DNBProcessMemoryDeallocate (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//----------------------------------------------------------------------
1211int
1212DNBProcessMemoryRegionInfo (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
1221std::string
1222DNBProcessGetProfileData (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
1231nub_bool_t
1232DNBProcessSetAsyncEnableProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec)
1233{
1234    MachProcessSP procSP;
1235    if (GetProcessSP (pid, procSP))
1236    {
1237        procSP->SetAsyncEnableProfiling(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//----------------------------------------------------------------------
1248nub_size_t
1249DNBPrintf (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//----------------------------------------------------------------------
1753nub_size_t
1754DNBProcessGetNumThreads (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//----------------------------------------------------------------------
1765nub_thread_t
1766DNBProcessGetCurrentThread (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//----------------------------------------------------------------------
1777nub_thread_t
1778DNBProcessSetCurrentThread (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//----------------------------------------------------------------------
1791nub_bool_t
1792DNBThreadGetStopReason (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//----------------------------------------------------------------------
1807const char *
1808DNBThreadGetInfo (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//----------------------------------------------------------------------
1819nub_thread_t
1820DNBProcessGetThreadAtIndex (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//----------------------------------------------------------------------
1831nub_bool_t
1832DNBProcessSyncThreadState (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
1841nub_addr_t
1842DNBProcessGetSharedLibraryInfoAddress (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
1852nub_bool_t
1853DNBProcessSharedLibrariesUpdated(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//----------------------------------------------------------------------
1869nub_size_t
1870DNBProcessGetSharedLibraryInfo (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//----------------------------------------------------------------------
1885const DNBRegisterSetInfo *
1886DNBGetRegisterSetInfo (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//----------------------------------------------------------------------
1895nub_bool_t
1896DNBThreadGetRegisterValueByID (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
1908nub_bool_t
1909DNBThreadSetRegisterValueByID (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
1920nub_size_t
1921DNBThreadGetRegisterContext (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
1934nub_size_t
1935DNBThreadSetRegisterContext (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//----------------------------------------------------------------------
1949nub_bool_t
1950DNBThreadGetRegisterValueByName (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//----------------------------------------------------------------------
1991nub_bool_t
1992DNBGetRegisterInfoByName (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//----------------------------------------------------------------------
2038nub_bool_t
2039DNBProcessSetNameToAddressCallback (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//----------------------------------------------------------------------
2055nub_bool_t
2056DNBProcessSetSharedLibraryInfoCallback (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
2067nub_addr_t
2068DNBProcessLookupAddress (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
2079nub_size_t
2080DNBProcessGetAvailableSTDOUT (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
2088nub_size_t
2089DNBProcessGetAvailableSTDERR (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
2097nub_size_t
2098DNBProcessGetAvailableProfileData (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
2106nub_size_t
2107DNBProcessGetStopCount (nub_process_t pid)
2108{
2109    MachProcessSP procSP;
2110    if (GetProcessSP (pid, procSP))
2111        return procSP->StopCount();
2112    return 0;
2113}
2114
2115uint32_t
2116DNBProcessGetCPUType (nub_process_t pid)
2117{
2118    MachProcessSP procSP;
2119    if (GetProcessSP (pid, procSP))
2120        return procSP->GetCPUType ();
2121    return 0;
2122
2123}
2124
2125nub_bool_t
2126DNBResolveExecutablePath (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
2199void
2200DNBInitialize()
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
2211void
2212DNBTerminate()
2213{
2214}
2215
2216nub_bool_t
2217DNBSetArchitecture (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