DNB.cpp revision f14269add7f6fb84da7ccfd866f8e9f7e81068be
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
1220std::string
1221DNBProcessGetProfileData (nub_process_t pid)
1222{
1223    MachProcessSP procSP;
1224    if (GetProcessSP (pid, procSP))
1225        return procSP->Task().GetProfileData();
1226
1227    return std::string("");
1228}
1229
1230nub_bool_t
1231DNBProcessSetAsyncEnableProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec)
1232{
1233    MachProcessSP procSP;
1234    if (GetProcessSP (pid, procSP))
1235    {
1236        procSP->SetAsyncEnableProfiling(enable, interval_usec);
1237        return true;
1238    }
1239
1240    return false;
1241}
1242
1243//----------------------------------------------------------------------
1244// Formatted output that uses memory and registers from process and
1245// thread in place of arguments.
1246//----------------------------------------------------------------------
1247nub_size_t
1248DNBPrintf (nub_process_t pid, nub_thread_t tid, nub_addr_t base_addr, FILE *file, const char *format)
1249{
1250    if (file == NULL)
1251        return 0;
1252    enum printf_flags
1253    {
1254        alternate_form          = (1 << 0),
1255        zero_padding            = (1 << 1),
1256        negative_field_width    = (1 << 2),
1257        blank_space             = (1 << 3),
1258        show_sign               = (1 << 4),
1259        show_thousands_separator= (1 << 5),
1260    };
1261
1262    enum printf_length_modifiers
1263    {
1264        length_mod_h            = (1 << 0),
1265        length_mod_hh           = (1 << 1),
1266        length_mod_l            = (1 << 2),
1267        length_mod_ll           = (1 << 3),
1268        length_mod_L            = (1 << 4),
1269        length_mod_j            = (1 << 5),
1270        length_mod_t            = (1 << 6),
1271        length_mod_z            = (1 << 7),
1272        length_mod_q            = (1 << 8),
1273    };
1274
1275    nub_addr_t addr = base_addr;
1276    char *end_format = (char*)format + strlen(format);
1277    char *end = NULL;    // For strtoXXXX calls;
1278    std::basic_string<uint8_t> buf;
1279    nub_size_t total_bytes_read = 0;
1280    DNBDataRef data;
1281    const char *f;
1282    for (f = format; *f != '\0' && f < end_format; f++)
1283    {
1284        char ch = *f;
1285        switch (ch)
1286        {
1287        case '%':
1288            {
1289                f++;    // Skip the '%' character
1290//                int min_field_width = 0;
1291//                int precision = 0;
1292                //uint32_t flags = 0;
1293                uint32_t length_modifiers = 0;
1294                uint32_t byte_size = 0;
1295                uint32_t actual_byte_size = 0;
1296                bool is_string = false;
1297                bool is_register = false;
1298                DNBRegisterValue register_value;
1299                int64_t    register_offset = 0;
1300                nub_addr_t register_addr = INVALID_NUB_ADDRESS;
1301
1302                // Create the format string to use for this conversion specification
1303                // so we can remove and mprintf specific flags and formatters.
1304                std::string fprintf_format("%");
1305
1306                // Decode any flags
1307                switch (*f)
1308                {
1309                case '#': fprintf_format += *f++; break; //flags |= alternate_form;          break;
1310                case '0': fprintf_format += *f++; break; //flags |= zero_padding;            break;
1311                case '-': fprintf_format += *f++; break; //flags |= negative_field_width;    break;
1312                case ' ': fprintf_format += *f++; break; //flags |= blank_space;             break;
1313                case '+': fprintf_format += *f++; break; //flags |= show_sign;               break;
1314                case ',': fprintf_format += *f++; break; //flags |= show_thousands_separator;break;
1315                case '{':
1316                case '[':
1317                    {
1318                        // We have a register name specification that can take two forms:
1319                        // ${regname} or ${regname+offset}
1320                        //        The action is to read the register value and add the signed offset
1321                        //        (if any) and use that as the value to format.
1322                        // $[regname] or $[regname+offset]
1323                        //        The action is to read the register value and add the signed offset
1324                        //        (if any) and use the result as an address to dereference. The size
1325                        //        of what is dereferenced is specified by the actual byte size that
1326                        //        follows the minimum field width and precision (see comments below).
1327                        switch (*f)
1328                        {
1329                        case '{':
1330                        case '[':
1331                            {
1332                                char open_scope_ch = *f;
1333                                f++;
1334                                const char *reg_name = f;
1335                                size_t reg_name_length = strcspn(f, "+-}]");
1336                                if (reg_name_length > 0)
1337                                {
1338                                    std::string register_name(reg_name, reg_name_length);
1339                                    f += reg_name_length;
1340                                    register_offset = strtoll(f, &end, 0);
1341                                    if (f < end)
1342                                        f = end;
1343                                    if ((open_scope_ch == '{' && *f != '}') || (open_scope_ch == '[' && *f != ']'))
1344                                    {
1345                                        fprintf(file, "error: Invalid register format string. Valid formats are %%{regname} or %%{regname+offset}, %%[regname] or %%[regname+offset]\n");
1346                                        return total_bytes_read;
1347                                    }
1348                                    else
1349                                    {
1350                                        f++;
1351                                        if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, register_name.c_str(), &register_value))
1352                                        {
1353                                            // Set the address to dereference using the register value plus the offset
1354                                            switch (register_value.info.size)
1355                                            {
1356                                            default:
1357                                            case 0:
1358                                                fprintf (file, "error: unsupported register size of %u.\n", register_value.info.size);
1359                                                return total_bytes_read;
1360
1361                                            case 1:        register_addr = register_value.value.uint8  + register_offset; break;
1362                                            case 2:        register_addr = register_value.value.uint16 + register_offset; break;
1363                                            case 4:        register_addr = register_value.value.uint32 + register_offset; break;
1364                                            case 8:        register_addr = register_value.value.uint64 + register_offset; break;
1365                                            case 16:
1366                                                if (open_scope_ch == '[')
1367                                                {
1368                                                    fprintf (file, "error: register size (%u) too large for address.\n", register_value.info.size);
1369                                                    return total_bytes_read;
1370                                                }
1371                                                break;
1372                                            }
1373
1374                                            if (open_scope_ch == '{')
1375                                            {
1376                                                byte_size = register_value.info.size;
1377                                                is_register = true;    // value is in a register
1378
1379                                            }
1380                                            else
1381                                            {
1382                                                addr = register_addr;    // Use register value and offset as the address
1383                                            }
1384                                        }
1385                                        else
1386                                        {
1387                                            fprintf(file, "error: unable to read register '%s' for process %#.4x and thread %#.4x\n", register_name.c_str(), pid, tid);
1388                                            return total_bytes_read;
1389                                        }
1390                                    }
1391                                }
1392                            }
1393                            break;
1394
1395                        default:
1396                            fprintf(file, "error: %%$ must be followed by (regname + n) or [regname + n]\n");
1397                            return total_bytes_read;
1398                        }
1399                    }
1400                    break;
1401                }
1402
1403                // Check for a minimum field width
1404                if (isdigit(*f))
1405                {
1406                    //min_field_width = strtoul(f, &end, 10);
1407                    strtoul(f, &end, 10);
1408                    if (end > f)
1409                    {
1410                        fprintf_format.append(f, end - f);
1411                        f = end;
1412                    }
1413                }
1414
1415
1416                // Check for a precision
1417                if (*f == '.')
1418                {
1419                    f++;
1420                    if (isdigit(*f))
1421                    {
1422                        fprintf_format += '.';
1423                        //precision = strtoul(f, &end, 10);
1424                        strtoul(f, &end, 10);
1425                        if (end > f)
1426                        {
1427                            fprintf_format.append(f, end - f);
1428                            f = end;
1429                        }
1430                    }
1431                }
1432
1433
1434                // mprintf specific: read the optional actual byte size (abs)
1435                // after the standard minimum field width (mfw) and precision (prec).
1436                // Standard printf calls you can have "mfw.prec" or ".prec", but
1437                // mprintf can have "mfw.prec.abs", ".prec.abs" or "..abs". This is nice
1438                // for strings that may be in a fixed size buffer, but may not use all bytes
1439                // in that buffer for printable characters.
1440                if (*f == '.')
1441                {
1442                    f++;
1443                    actual_byte_size = strtoul(f, &end, 10);
1444                    if (end > f)
1445                    {
1446                        byte_size = actual_byte_size;
1447                        f = end;
1448                    }
1449                }
1450
1451                // Decode the length modifiers
1452                switch (*f)
1453                {
1454                case 'h':    // h and hh length modifiers
1455                    fprintf_format += *f++;
1456                    length_modifiers |= length_mod_h;
1457                    if (*f == 'h')
1458                    {
1459                        fprintf_format += *f++;
1460                        length_modifiers |= length_mod_hh;
1461                    }
1462                    break;
1463
1464                case 'l': // l and ll length modifiers
1465                    fprintf_format += *f++;
1466                    length_modifiers |= length_mod_l;
1467                    if (*f == 'h')
1468                    {
1469                        fprintf_format += *f++;
1470                        length_modifiers |= length_mod_ll;
1471                    }
1472                    break;
1473
1474                case 'L':    fprintf_format += *f++;    length_modifiers |= length_mod_L;    break;
1475                case 'j':    fprintf_format += *f++;    length_modifiers |= length_mod_j;    break;
1476                case 't':    fprintf_format += *f++;    length_modifiers |= length_mod_t;    break;
1477                case 'z':    fprintf_format += *f++;    length_modifiers |= length_mod_z;    break;
1478                case 'q':    fprintf_format += *f++;    length_modifiers |= length_mod_q;    break;
1479                }
1480
1481                // Decode the conversion specifier
1482                switch (*f)
1483                {
1484                case '_':
1485                    // mprintf specific format items
1486                    {
1487                        ++f;    // Skip the '_' character
1488                        switch (*f)
1489                        {
1490                        case 'a':    // Print the current address
1491                            ++f;
1492                            fprintf_format += "ll";
1493                            fprintf_format += *f;    // actual format to show address with folows the 'a' ("%_ax")
1494                            fprintf (file, fprintf_format.c_str(), addr);
1495                            break;
1496                        case 'o':    // offset from base address
1497                            ++f;
1498                            fprintf_format += "ll";
1499                            fprintf_format += *f;    // actual format to show address with folows the 'a' ("%_ox")
1500                            fprintf(file, fprintf_format.c_str(), addr - base_addr);
1501                            break;
1502                        default:
1503                            fprintf (file, "error: unsupported mprintf specific format character '%c'.\n", *f);
1504                            break;
1505                        }
1506                        continue;
1507                    }
1508                    break;
1509
1510                case 'D':
1511                case 'O':
1512                case 'U':
1513                    fprintf_format += *f;
1514                    if (byte_size == 0)
1515                        byte_size = sizeof(long int);
1516                    break;
1517
1518                case 'd':
1519                case 'i':
1520                case 'o':
1521                case 'u':
1522                case 'x':
1523                case 'X':
1524                    fprintf_format += *f;
1525                    if (byte_size == 0)
1526                    {
1527                        if (length_modifiers & length_mod_hh)
1528                            byte_size = sizeof(char);
1529                        else if (length_modifiers & length_mod_h)
1530                            byte_size = sizeof(short);
1531                        else if (length_modifiers & length_mod_ll)
1532                            byte_size = sizeof(long long);
1533                        else if (length_modifiers & length_mod_l)
1534                            byte_size = sizeof(long);
1535                        else
1536                            byte_size = sizeof(int);
1537                    }
1538                    break;
1539
1540                case 'a':
1541                case 'A':
1542                case 'f':
1543                case 'F':
1544                case 'e':
1545                case 'E':
1546                case 'g':
1547                case 'G':
1548                    fprintf_format += *f;
1549                    if (byte_size == 0)
1550                    {
1551                        if (length_modifiers & length_mod_L)
1552                            byte_size = sizeof(long double);
1553                        else
1554                            byte_size = sizeof(double);
1555                    }
1556                    break;
1557
1558                case 'c':
1559                    if ((length_modifiers & length_mod_l) == 0)
1560                    {
1561                        fprintf_format += *f;
1562                        if (byte_size == 0)
1563                            byte_size = sizeof(char);
1564                        break;
1565                    }
1566                    // Fall through to 'C' modifier below...
1567
1568                case 'C':
1569                    fprintf_format += *f;
1570                    if (byte_size == 0)
1571                        byte_size = sizeof(wchar_t);
1572                    break;
1573
1574                case 's':
1575                    fprintf_format += *f;
1576                    if (is_register || byte_size == 0)
1577                        is_string = 1;
1578                    break;
1579
1580                case 'p':
1581                    fprintf_format += *f;
1582                    if (byte_size == 0)
1583                        byte_size = sizeof(void*);
1584                    break;
1585                }
1586
1587                if (is_string)
1588                {
1589                    std::string mem_string;
1590                    const size_t string_buf_len = 4;
1591                    char string_buf[string_buf_len+1];
1592                    char *string_buf_end = string_buf + string_buf_len;
1593                    string_buf[string_buf_len] = '\0';
1594                    nub_size_t bytes_read;
1595                    nub_addr_t str_addr = is_register ? register_addr : addr;
1596                    while ((bytes_read = DNBProcessMemoryRead(pid, str_addr, string_buf_len, &string_buf[0])) > 0)
1597                    {
1598                        // Did we get a NULL termination character yet?
1599                        if (strchr(string_buf, '\0') == string_buf_end)
1600                        {
1601                            // no NULL terminator yet, append as a std::string
1602                            mem_string.append(string_buf, string_buf_len);
1603                            str_addr += string_buf_len;
1604                        }
1605                        else
1606                        {
1607                            // yep
1608                            break;
1609                        }
1610                    }
1611                    // Append as a C-string so we don't get the extra NULL
1612                    // characters in the temp buffer (since it was resized)
1613                    mem_string += string_buf;
1614                    size_t mem_string_len = mem_string.size() + 1;
1615                    fprintf(file, fprintf_format.c_str(), mem_string.c_str());
1616                    if (mem_string_len > 0)
1617                    {
1618                        if (!is_register)
1619                        {
1620                            addr += mem_string_len;
1621                            total_bytes_read += mem_string_len;
1622                        }
1623                    }
1624                    else
1625                        return total_bytes_read;
1626                }
1627                else
1628                if (byte_size > 0)
1629                {
1630                    buf.resize(byte_size);
1631                    nub_size_t bytes_read = 0;
1632                    if (is_register)
1633                        bytes_read = register_value.info.size;
1634                    else
1635                        bytes_read = DNBProcessMemoryRead(pid, addr, buf.size(), &buf[0]);
1636                    if (bytes_read > 0)
1637                    {
1638                        if (!is_register)
1639                            total_bytes_read += bytes_read;
1640
1641                        if (bytes_read == byte_size)
1642                        {
1643                            switch (*f)
1644                            {
1645                            case 'd':
1646                            case 'i':
1647                            case 'o':
1648                            case 'u':
1649                            case 'X':
1650                            case 'x':
1651                            case 'a':
1652                            case 'A':
1653                            case 'f':
1654                            case 'F':
1655                            case 'e':
1656                            case 'E':
1657                            case 'g':
1658                            case 'G':
1659                            case 'p':
1660                            case 'c':
1661                            case 'C':
1662                                {
1663                                    if (is_register)
1664                                        data.SetData(&register_value.value.v_uint8[0], register_value.info.size);
1665                                    else
1666                                        data.SetData(&buf[0], bytes_read);
1667                                    DNBDataRef::offset_t data_offset = 0;
1668                                    if (byte_size <= 4)
1669                                    {
1670                                        uint32_t u32 = data.GetMax32(&data_offset, byte_size);
1671                                        // Show the actual byte width when displaying hex
1672                                        fprintf(file, fprintf_format.c_str(), u32);
1673                                    }
1674                                    else if (byte_size <= 8)
1675                                    {
1676                                        uint64_t u64 = data.GetMax64(&data_offset, byte_size);
1677                                        // Show the actual byte width when displaying hex
1678                                        fprintf(file, fprintf_format.c_str(), u64);
1679                                    }
1680                                    else
1681                                    {
1682                                        fprintf(file, "error: integer size not supported, must be 8 bytes or less (%u bytes).\n", byte_size);
1683                                    }
1684                                    if (!is_register)
1685                                        addr += byte_size;
1686                                }
1687                                break;
1688
1689                            case 's':
1690                                fprintf(file, fprintf_format.c_str(), buf.c_str());
1691                                addr += byte_size;
1692                                break;
1693
1694                            default:
1695                                fprintf(file, "error: unsupported conversion specifier '%c'.\n", *f);
1696                                break;
1697                            }
1698                        }
1699                    }
1700                }
1701                else
1702                    return total_bytes_read;
1703            }
1704            break;
1705
1706        case '\\':
1707            {
1708                f++;
1709                switch (*f)
1710                {
1711                case 'e': ch = '\e'; break;
1712                case 'a': ch = '\a'; break;
1713                case 'b': ch = '\b'; break;
1714                case 'f': ch = '\f'; break;
1715                case 'n': ch = '\n'; break;
1716                case 'r': ch = '\r'; break;
1717                case 't': ch = '\t'; break;
1718                case 'v': ch = '\v'; break;
1719                case '\'': ch = '\''; break;
1720                case '\\': ch = '\\'; break;
1721                case '0':
1722                case '1':
1723                case '2':
1724                case '3':
1725                case '4':
1726                case '5':
1727                case '6':
1728                case '7':
1729                    ch = strtoul(f, &end, 8);
1730                    f = end;
1731                    break;
1732                default:
1733                    ch = *f;
1734                    break;
1735                }
1736                fputc(ch, file);
1737            }
1738            break;
1739
1740        default:
1741            fputc(ch, file);
1742            break;
1743        }
1744    }
1745    return total_bytes_read;
1746}
1747
1748
1749//----------------------------------------------------------------------
1750// Get the number of threads for the specified process.
1751//----------------------------------------------------------------------
1752nub_size_t
1753DNBProcessGetNumThreads (nub_process_t pid)
1754{
1755    MachProcessSP procSP;
1756    if (GetProcessSP (pid, procSP))
1757        return procSP->GetNumThreads();
1758    return 0;
1759}
1760
1761//----------------------------------------------------------------------
1762// Get the thread ID of the current thread.
1763//----------------------------------------------------------------------
1764nub_thread_t
1765DNBProcessGetCurrentThread (nub_process_t pid)
1766{
1767    MachProcessSP procSP;
1768    if (GetProcessSP (pid, procSP))
1769        return procSP->GetCurrentThread();
1770    return 0;
1771}
1772
1773//----------------------------------------------------------------------
1774// Change the current thread.
1775//----------------------------------------------------------------------
1776nub_thread_t
1777DNBProcessSetCurrentThread (nub_process_t pid, nub_thread_t tid)
1778{
1779    MachProcessSP procSP;
1780    if (GetProcessSP (pid, procSP))
1781        return procSP->SetCurrentThread (tid);
1782    return INVALID_NUB_THREAD;
1783}
1784
1785
1786//----------------------------------------------------------------------
1787// Dump a string describing a thread's stop reason to the specified file
1788// handle
1789//----------------------------------------------------------------------
1790nub_bool_t
1791DNBThreadGetStopReason (nub_process_t pid, nub_thread_t tid, struct DNBThreadStopInfo *stop_info)
1792{
1793    MachProcessSP procSP;
1794    if (GetProcessSP (pid, procSP))
1795        return procSP->GetThreadStoppedReason (tid, stop_info);
1796    return false;
1797}
1798
1799//----------------------------------------------------------------------
1800// Return string description for the specified thread.
1801//
1802// RETURNS: NULL if the thread isn't valid, else a NULL terminated C
1803// string from a static buffer that must be copied prior to subsequent
1804// calls.
1805//----------------------------------------------------------------------
1806const char *
1807DNBThreadGetInfo (nub_process_t pid, nub_thread_t tid)
1808{
1809    MachProcessSP procSP;
1810    if (GetProcessSP (pid, procSP))
1811        return procSP->GetThreadInfo (tid);
1812    return NULL;
1813}
1814
1815//----------------------------------------------------------------------
1816// Get the thread ID given a thread index.
1817//----------------------------------------------------------------------
1818nub_thread_t
1819DNBProcessGetThreadAtIndex (nub_process_t pid, size_t thread_idx)
1820{
1821    MachProcessSP procSP;
1822    if (GetProcessSP (pid, procSP))
1823        return procSP->GetThreadAtIndex (thread_idx);
1824    return INVALID_NUB_THREAD;
1825}
1826
1827//----------------------------------------------------------------------
1828// Do whatever is needed to sync the thread's register state with it's kernel values.
1829//----------------------------------------------------------------------
1830nub_bool_t
1831DNBProcessSyncThreadState (nub_process_t pid, nub_thread_t tid)
1832{
1833    MachProcessSP procSP;
1834    if (GetProcessSP (pid, procSP))
1835        return procSP->SyncThreadState (tid);
1836    return false;
1837
1838}
1839
1840nub_addr_t
1841DNBProcessGetSharedLibraryInfoAddress (nub_process_t pid)
1842{
1843    MachProcessSP procSP;
1844    DNBError err;
1845    if (GetProcessSP (pid, procSP))
1846        return procSP->Task().GetDYLDAllImageInfosAddress (err);
1847    return INVALID_NUB_ADDRESS;
1848}
1849
1850
1851nub_bool_t
1852DNBProcessSharedLibrariesUpdated(nub_process_t pid)
1853{
1854    MachProcessSP procSP;
1855    if (GetProcessSP (pid, procSP))
1856    {
1857        procSP->SharedLibrariesUpdated ();
1858        return true;
1859    }
1860    return false;
1861}
1862
1863//----------------------------------------------------------------------
1864// Get the current shared library information for a process. Only return
1865// the shared libraries that have changed since the last shared library
1866// state changed event if only_changed is non-zero.
1867//----------------------------------------------------------------------
1868nub_size_t
1869DNBProcessGetSharedLibraryInfo (nub_process_t pid, nub_bool_t only_changed, struct DNBExecutableImageInfo **image_infos)
1870{
1871    MachProcessSP procSP;
1872    if (GetProcessSP (pid, procSP))
1873        return procSP->CopyImageInfos (image_infos, only_changed);
1874
1875    // If we have no process, then return NULL for the shared library info
1876    // and zero for shared library count
1877    *image_infos = NULL;
1878    return 0;
1879}
1880
1881//----------------------------------------------------------------------
1882// Get the register set information for a specific thread.
1883//----------------------------------------------------------------------
1884const DNBRegisterSetInfo *
1885DNBGetRegisterSetInfo (nub_size_t *num_reg_sets)
1886{
1887    return DNBArchProtocol::GetRegisterSetInfo (num_reg_sets);
1888}
1889
1890
1891//----------------------------------------------------------------------
1892// Read a register value by register set and register index.
1893//----------------------------------------------------------------------
1894nub_bool_t
1895DNBThreadGetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value)
1896{
1897    MachProcessSP procSP;
1898    ::bzero (value, sizeof(DNBRegisterValue));
1899    if (GetProcessSP (pid, procSP))
1900    {
1901        if (tid != INVALID_NUB_THREAD)
1902            return procSP->GetRegisterValue (tid, set, reg, value);
1903    }
1904    return false;
1905}
1906
1907nub_bool_t
1908DNBThreadSetRegisterValueByID (nub_process_t pid, nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value)
1909{
1910    if (tid != INVALID_NUB_THREAD)
1911    {
1912        MachProcessSP procSP;
1913        if (GetProcessSP (pid, procSP))
1914            return procSP->SetRegisterValue (tid, set, reg, value);
1915    }
1916    return false;
1917}
1918
1919nub_size_t
1920DNBThreadGetRegisterContext (nub_process_t pid, nub_thread_t tid, void *buf, size_t buf_len)
1921{
1922    MachProcessSP procSP;
1923    if (GetProcessSP (pid, procSP))
1924    {
1925        if (tid != INVALID_NUB_THREAD)
1926            return procSP->GetThreadList().GetRegisterContext (tid, buf, buf_len);
1927    }
1928    ::bzero (buf, buf_len);
1929    return 0;
1930
1931}
1932
1933nub_size_t
1934DNBThreadSetRegisterContext (nub_process_t pid, nub_thread_t tid, const void *buf, size_t buf_len)
1935{
1936    MachProcessSP procSP;
1937    if (GetProcessSP (pid, procSP))
1938    {
1939        if (tid != INVALID_NUB_THREAD)
1940            return procSP->GetThreadList().SetRegisterContext (tid, buf, buf_len);
1941    }
1942    return 0;
1943}
1944
1945//----------------------------------------------------------------------
1946// Read a register value by name.
1947//----------------------------------------------------------------------
1948nub_bool_t
1949DNBThreadGetRegisterValueByName (nub_process_t pid, nub_thread_t tid, uint32_t reg_set, const char *reg_name, DNBRegisterValue *value)
1950{
1951    MachProcessSP procSP;
1952    ::bzero (value, sizeof(DNBRegisterValue));
1953    if (GetProcessSP (pid, procSP))
1954    {
1955        const struct DNBRegisterSetInfo *set_info;
1956        nub_size_t num_reg_sets = 0;
1957        set_info = DNBGetRegisterSetInfo (&num_reg_sets);
1958        if (set_info)
1959        {
1960            uint32_t set = reg_set;
1961            uint32_t reg;
1962            if (set == REGISTER_SET_ALL)
1963            {
1964                for (set = 1; set < num_reg_sets; ++set)
1965                {
1966                    for (reg = 0; reg < set_info[set].num_registers; ++reg)
1967                    {
1968                        if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1969                            return procSP->GetRegisterValue (tid, set, reg, value);
1970                    }
1971                }
1972            }
1973            else
1974            {
1975                for (reg = 0; reg < set_info[set].num_registers; ++reg)
1976                {
1977                    if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
1978                        return procSP->GetRegisterValue (tid, set, reg, value);
1979                }
1980            }
1981        }
1982    }
1983    return false;
1984}
1985
1986
1987//----------------------------------------------------------------------
1988// Read a register set and register number from the register name.
1989//----------------------------------------------------------------------
1990nub_bool_t
1991DNBGetRegisterInfoByName (const char *reg_name, DNBRegisterInfo* info)
1992{
1993    const struct DNBRegisterSetInfo *set_info;
1994    nub_size_t num_reg_sets = 0;
1995    set_info = DNBGetRegisterSetInfo (&num_reg_sets);
1996    if (set_info)
1997    {
1998        uint32_t set, reg;
1999        for (set = 1; set < num_reg_sets; ++set)
2000        {
2001            for (reg = 0; reg < set_info[set].num_registers; ++reg)
2002            {
2003                if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
2004                {
2005                    *info = set_info[set].registers[reg];
2006                    return true;
2007                }
2008            }
2009        }
2010
2011        for (set = 1; set < num_reg_sets; ++set)
2012        {
2013            uint32_t reg;
2014            for (reg = 0; reg < set_info[set].num_registers; ++reg)
2015            {
2016                if (set_info[set].registers[reg].alt == NULL)
2017                    continue;
2018
2019                if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0)
2020                {
2021                    *info = set_info[set].registers[reg];
2022                    return true;
2023                }
2024            }
2025        }
2026    }
2027
2028    ::bzero (info, sizeof(DNBRegisterInfo));
2029    return false;
2030}
2031
2032
2033//----------------------------------------------------------------------
2034// Set the name to address callback function that this nub can use
2035// for any name to address lookups that are needed.
2036//----------------------------------------------------------------------
2037nub_bool_t
2038DNBProcessSetNameToAddressCallback (nub_process_t pid, DNBCallbackNameToAddress callback, void *baton)
2039{
2040    MachProcessSP procSP;
2041    if (GetProcessSP (pid, procSP))
2042    {
2043        procSP->SetNameToAddressCallback (callback, baton);
2044        return true;
2045    }
2046    return false;
2047}
2048
2049
2050//----------------------------------------------------------------------
2051// Set the name to address callback function that this nub can use
2052// for any name to address lookups that are needed.
2053//----------------------------------------------------------------------
2054nub_bool_t
2055DNBProcessSetSharedLibraryInfoCallback (nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback, void  *baton)
2056{
2057    MachProcessSP procSP;
2058    if (GetProcessSP (pid, procSP))
2059    {
2060        procSP->SetSharedLibraryInfoCallback (callback, baton);
2061        return true;
2062    }
2063    return false;
2064}
2065
2066nub_addr_t
2067DNBProcessLookupAddress (nub_process_t pid, const char *name, const char *shlib)
2068{
2069    MachProcessSP procSP;
2070    if (GetProcessSP (pid, procSP))
2071    {
2072        return procSP->LookupSymbol (name, shlib);
2073    }
2074    return INVALID_NUB_ADDRESS;
2075}
2076
2077
2078nub_size_t
2079DNBProcessGetAvailableSTDOUT (nub_process_t pid, char *buf, nub_size_t buf_size)
2080{
2081    MachProcessSP procSP;
2082    if (GetProcessSP (pid, procSP))
2083        return procSP->GetAvailableSTDOUT (buf, buf_size);
2084    return 0;
2085}
2086
2087nub_size_t
2088DNBProcessGetAvailableSTDERR (nub_process_t pid, char *buf, nub_size_t buf_size)
2089{
2090    MachProcessSP procSP;
2091    if (GetProcessSP (pid, procSP))
2092        return procSP->GetAvailableSTDERR (buf, buf_size);
2093    return 0;
2094}
2095
2096nub_size_t
2097DNBProcessGetAvailableProfileData (nub_process_t pid, char *buf, nub_size_t buf_size)
2098{
2099    MachProcessSP procSP;
2100    if (GetProcessSP (pid, procSP))
2101        return procSP->GetAsyncProfileData (buf, buf_size);
2102    return 0;
2103}
2104
2105nub_size_t
2106DNBProcessGetStopCount (nub_process_t pid)
2107{
2108    MachProcessSP procSP;
2109    if (GetProcessSP (pid, procSP))
2110        return procSP->StopCount();
2111    return 0;
2112}
2113
2114uint32_t
2115DNBProcessGetCPUType (nub_process_t pid)
2116{
2117    MachProcessSP procSP;
2118    if (GetProcessSP (pid, procSP))
2119        return procSP->GetCPUType ();
2120    return 0;
2121
2122}
2123
2124nub_bool_t
2125DNBResolveExecutablePath (const char *path, char *resolved_path, size_t resolved_path_size)
2126{
2127    if (path == NULL || path[0] == '\0')
2128        return false;
2129
2130    char max_path[PATH_MAX];
2131    std::string result;
2132    CFString::GlobPath(path, result);
2133
2134    if (result.empty())
2135        result = path;
2136
2137    struct stat path_stat;
2138    if (::stat(path, &path_stat) == 0)
2139    {
2140        if ((path_stat.st_mode & S_IFMT) == S_IFDIR)
2141        {
2142            CFBundle bundle (path);
2143            CFReleaser<CFURLRef> url(bundle.CopyExecutableURL ());
2144            if (url.get())
2145            {
2146                if (::CFURLGetFileSystemRepresentation (url.get(), true, (UInt8*)resolved_path, resolved_path_size))
2147                    return true;
2148            }
2149        }
2150    }
2151
2152    if (realpath(path, max_path))
2153    {
2154        // Found the path relatively...
2155        ::strncpy(resolved_path, max_path, resolved_path_size);
2156        return strlen(resolved_path) + 1 < resolved_path_size;
2157    }
2158    else
2159    {
2160        // Not a relative path, check the PATH environment variable if the
2161        const char *PATH = getenv("PATH");
2162        if (PATH)
2163        {
2164            const char *curr_path_start = PATH;
2165            const char *curr_path_end;
2166            while (curr_path_start && *curr_path_start)
2167            {
2168                curr_path_end = strchr(curr_path_start, ':');
2169                if (curr_path_end == NULL)
2170                {
2171                    result.assign(curr_path_start);
2172                    curr_path_start = NULL;
2173                }
2174                else if (curr_path_end > curr_path_start)
2175                {
2176                    size_t len = curr_path_end - curr_path_start;
2177                    result.assign(curr_path_start, len);
2178                    curr_path_start += len + 1;
2179                }
2180                else
2181                    break;
2182
2183                result += '/';
2184                result += path;
2185                struct stat s;
2186                if (stat(result.c_str(), &s) == 0)
2187                {
2188                    ::strncpy(resolved_path, result.c_str(), resolved_path_size);
2189                    return result.size() + 1 < resolved_path_size;
2190                }
2191            }
2192        }
2193    }
2194    return false;
2195}
2196
2197
2198void
2199DNBInitialize()
2200{
2201    DNBLogThreadedIf (LOG_PROCESS, "DNBInitialize ()");
2202#if defined (__i386__) || defined (__x86_64__)
2203    DNBArchImplI386::Initialize();
2204    DNBArchImplX86_64::Initialize();
2205#elif defined (__arm__)
2206    DNBArchMachARM::Initialize();
2207#endif
2208}
2209
2210void
2211DNBTerminate()
2212{
2213}
2214
2215nub_bool_t
2216DNBSetArchitecture (const char *arch)
2217{
2218    if (arch && arch[0])
2219    {
2220        if (strcasecmp (arch, "i386") == 0)
2221            return DNBArchProtocol::SetArchitecture (CPU_TYPE_I386);
2222        else if (strcasecmp (arch, "x86_64") == 0)
2223            return DNBArchProtocol::SetArchitecture (CPU_TYPE_X86_64);
2224        else if (strstr (arch, "arm") == arch)
2225            return DNBArchProtocol::SetArchitecture (CPU_TYPE_ARM);
2226    }
2227    return false;
2228}
2229