blob: a0d7d35dd651ac0b18bfe8213351a29a05b5b657 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- MachProcess.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 6/15/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "DNB.h"
15#include <mach/mach.h>
16#include <spawn.h>
17#include <sys/fcntl.h>
18#include <sys/types.h>
19#include <sys/ptrace.h>
20#include <sys/stat.h>
21#include <unistd.h>
22#include "MacOSX/CFUtils.h"
23#include "SysSignal.h"
24
25#include <algorithm>
26#include <map>
27
28#include "DNBDataRef.h"
29#include "DNBLog.h"
30#include "DNBThreadResumeActions.h"
31#include "DNBTimer.h"
32#include "MachProcess.h"
33#include "PseudoTerminal.h"
34
35#include "CFBundle.h"
36#include "CFData.h"
37#include "CFString.h"
38
39static CFStringRef CopyBundleIDForPath (const char *app_buncle_path, DNBError &err_str);
40
41#if defined (__arm__)
42
43#include <CoreFoundation/CoreFoundation.h>
44#include <SpringBoardServices/SpringBoardServer.h>
45#include <SpringBoardServices/SBSWatchdogAssertion.h>
46
47
48static bool
49IsSBProcess (nub_process_t pid)
50{
51 bool opt_runningApps = true;
52 bool opt_debuggable = false;
53
54 CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));
55 if (sbsAppIDs.get() != NULL)
56 {
57 CFIndex count = ::CFArrayGetCount (sbsAppIDs.get());
58 CFIndex i = 0;
59 for (i = 0; i < count; i++)
60 {
61 CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
62
63 // Get the process id for the app (if there is one)
64 pid_t sbs_pid = INVALID_NUB_PROCESS;
65 if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE)
66 {
67 if (sbs_pid == pid)
68 return true;
69 }
70 }
71 }
72 return false;
73}
74
75
76#endif
77
78#if 0
79#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
80#else
81#define DEBUG_LOG(fmt, ...)
82#endif
83
84#ifndef MACH_PROCESS_USE_POSIX_SPAWN
85#define MACH_PROCESS_USE_POSIX_SPAWN 1
86#endif
87
88
89MachProcess::MachProcess() :
90 m_pid (0),
91 m_child_stdin (-1),
92 m_child_stdout (-1),
93 m_child_stderr (-1),
94 m_path (),
95 m_args (),
96 m_task (this),
97 m_flags (eMachProcessFlagsNone),
98 m_stdio_thread (0),
99 m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE),
100 m_stdout_data (),
101 m_threadList (),
102 m_exception_messages (),
103 m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000104 m_state (eStateUnloaded),
105 m_state_mutex (PTHREAD_MUTEX_RECURSIVE),
106 m_events (0, kAllEventsMask),
107 m_breakpoints (),
108 m_watchpoints (),
109 m_name_to_addr_callback(NULL),
110 m_name_to_addr_baton(NULL),
111 m_image_infos_callback(NULL),
112 m_image_infos_baton(NULL)
113{
114 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
115}
116
117MachProcess::~MachProcess()
118{
119 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
120 Clear();
121}
122
123pid_t
124MachProcess::SetProcessID(pid_t pid)
125{
126 // Free any previous process specific data or resources
127 Clear();
128 // Set the current PID appropriately
129 if (pid == 0)
130 m_pid == ::getpid ();
131 else
132 m_pid = pid;
133 return m_pid; // Return actualy PID in case a zero pid was passed in
134}
135
136nub_state_t
137MachProcess::GetState()
138{
139 // If any other threads access this we will need a mutex for it
140 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
141 return m_state;
142}
143
144const char *
145MachProcess::ThreadGetName(nub_thread_t tid)
146{
147 return m_threadList.GetName(tid);
148}
149
150nub_state_t
151MachProcess::ThreadGetState(nub_thread_t tid)
152{
153 return m_threadList.GetState(tid);
154}
155
156
157nub_size_t
158MachProcess::GetNumThreads () const
159{
160 return m_threadList.NumThreads();
161}
162
163nub_thread_t
164MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const
165{
166 return m_threadList.ThreadIDAtIndex(thread_idx);
167}
168
169uint32_t
170MachProcess::GetThreadIndexFromThreadID (nub_thread_t tid)
171{
172 return m_threadList.GetThreadIndexByID(tid);
173}
174
175nub_thread_t
176MachProcess::GetCurrentThread ()
177{
178 return m_threadList.CurrentThreadID();
179}
180
181nub_thread_t
182MachProcess::SetCurrentThread(nub_thread_t tid)
183{
184 return m_threadList.SetCurrentThread(tid);
185}
186
187bool
188MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
189{
190 return m_threadList.GetThreadStoppedReason(tid, stop_info);
191}
192
193void
194MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const
195{
196 return m_threadList.DumpThreadStoppedReason(tid);
197}
198
199const char *
200MachProcess::GetThreadInfo(nub_thread_t tid) const
201{
202 return m_threadList.GetThreadInfo(tid);
203}
204
205const DNBRegisterSetInfo *
206MachProcess::GetRegisterSetInfo(nub_thread_t tid, nub_size_t *num_reg_sets ) const
207{
208 return DNBArch::GetRegisterSetInfo (num_reg_sets);
209}
210
211bool
212MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const
213{
214 return m_threadList.GetRegisterValue(tid, set, reg, value);
215}
216
217bool
218MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const
219{
220 return m_threadList.SetRegisterValue(tid, set, reg, value);
221}
222
223void
224MachProcess::SetState(nub_state_t new_state)
225{
226 // If any other threads access this we will need a mutex for it
227 uint32_t event_mask = 0;
228
229 // Scope for mutex locker
230 {
231 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
232 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state));
233
234 const nub_state_t old_state = m_state;
235
236 if (old_state != new_state)
237 {
238 if (NUB_STATE_IS_STOPPED(new_state))
239 event_mask = eEventProcessStoppedStateChanged;
240 else
241 event_mask = eEventProcessRunningStateChanged;
242
243 m_state = new_state;
244 if (new_state == eStateStopped)
245 m_stop_count++;
246 }
247 }
248
249 if (event_mask != 0)
250 {
251 m_events.SetEvents (event_mask);
252
253 // Wait for the event bit to reset if a reset ACK is requested
254 m_events.WaitForResetAck(event_mask);
255 }
256
257}
258
259void
260MachProcess::Clear()
261{
262 // Clear any cached thread list while the pid and task are still valid
263
264 m_task.Clear();
265 // Now clear out all member variables
266 m_pid = INVALID_NUB_PROCESS;
267 CloseChildFileDescriptors();
268 m_path.clear();
269 m_args.clear();
270 SetState(eStateUnloaded);
271 m_flags = eMachProcessFlagsNone;
272 m_stop_count = 0;
273 m_threadList.Clear();
274 {
275 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
276 m_exception_messages.clear();
277 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000278}
279
280
281bool
282MachProcess::StartSTDIOThread()
283{
284 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
285 // Create the thread that watches for the child STDIO
Greg Clayton3382c2c2010-07-30 23:14:42 +0000286 return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000287}
288
289
290nub_addr_t
291MachProcess::LookupSymbol(const char *name, const char *shlib)
292{
293 if (m_name_to_addr_callback != NULL && name && name[0])
294 return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton);
295 return INVALID_NUB_ADDRESS;
296}
297
298bool
299MachProcess::Resume (const DNBThreadResumeActions& thread_actions)
300{
301 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
302 nub_state_t state = GetState();
303
304 if (CanResume(state))
305 {
306 PrivateResume(thread_actions);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000307 return true;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000308 }
309 else if (state == eStateRunning)
310 {
311 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort());
Greg Clayton3382c2c2010-07-30 23:14:42 +0000312 return true;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000313 }
Greg Clayton3382c2c2010-07-30 23:14:42 +0000314 DNBLogThreadedIf(LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort());
315 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000316}
317
318bool
319MachProcess::Kill (const struct timespec *timeout_abstime)
320{
321 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
322 nub_state_t state = DoSIGSTOP(true);
323 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state));
324 errno = 0;
325 ::ptrace (PT_KILL, m_pid, 0, 0);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000326 DNBError err;
327 err.SetErrorToErrno();
328 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", err.Error(), err.AsString());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000329 PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
330 return true;
331}
332
333bool
334MachProcess::Signal (int signal, const struct timespec *timeout_abstime)
335{
336 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime);
337 nub_state_t state = GetState();
338 if (::kill (ProcessID(), signal) == 0)
339 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000340 // If we were running and we have a timeout, wait for the signal to stop
341 if (IsRunning(state) && timeout_abstime)
342 {
343 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime);
344 m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime);
345 state = GetState();
346 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state));
347 return !IsRunning (state);
348 }
349 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000350 return true;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000351 }
Greg Clayton3382c2c2010-07-30 23:14:42 +0000352 DNBError err(errno, DNBError::POSIX);
353 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
354 return false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000355
356}
357
358nub_state_t
359MachProcess::DoSIGSTOP (bool clear_bps_and_wps)
360{
361 nub_state_t state = GetState();
362 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state));
363
364 if (!IsRunning(state))
365 {
366 if (clear_bps_and_wps)
367 {
368 DisableAllBreakpoints (true);
369 DisableAllWatchpoints (true);
370 clear_bps_and_wps = false;
371 }
372
373 // Resume our process
374 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state));
375 PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
376
377 // Reset the event that says we were indeed running
378 m_events.ResetEvents(eEventProcessRunningStateChanged);
379 state = GetState();
380 }
381
382 // We need to be stopped in order to be able to detach, so we need
383 // to send ourselves a SIGSTOP
384
385 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state));
386 struct timespec sigstop_timeout;
387 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
388 Signal (SIGSTOP, &sigstop_timeout);
389 if (clear_bps_and_wps)
390 {
391 DisableAllBreakpoints (true);
392 DisableAllWatchpoints (true);
393 clear_bps_and_wps = false;
394 }
395 return GetState();
396}
397
398bool
399MachProcess::Detach()
400{
401 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
402
403 nub_state_t state = DoSIGSTOP(true);
404 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state));
405
406
407 // Don't reply to our SIGSTOP exception, just make sure no threads
408 // are still suspended.
409 PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
410
411
412 // Detach from our process
413 errno = 0;
414 nub_process_t pid = m_pid;
415 ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000416 DNBError err(errno, DNBError::POSIX);
417 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail())
418 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000419
420 // Resume our task
421 m_task.Resume();
422
423 SetState(eStateDetached);
424
425 // NULL our task out as we have already retored all exception ports
426 m_task.Clear();
427
428 // Clear out any notion of the process we once were
429 Clear();
430 return true;
431}
432
433
434nub_size_t
435MachProcess::RemoveTrapsFromBuffer (nub_addr_t addr, nub_size_t size, uint8_t *buf) const
436{
437 nub_size_t bytes_removed = 0;
438 const DNBBreakpoint *bp;
439 nub_addr_t intersect_addr;
440 nub_size_t intersect_size;
441 nub_size_t opcode_offset;
442 nub_size_t idx;
443 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
444 {
445 if (bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset))
446 {
447 assert(addr <= intersect_addr && intersect_addr < addr + size);
448 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
449 assert(opcode_offset + intersect_size <= bp->ByteSize());
450 nub_size_t buf_offset = intersect_addr - addr;
451 ::memcpy(buf + buf_offset, bp->SavedOpcodeBytes() + opcode_offset, intersect_size);
452 }
453 }
454 return bytes_removed;
455}
456
457//----------------------------------------------------------------------
458// ReadMemory from the MachProcess level will always remove any software
459// breakpoints from the memory buffer before returning. If you wish to
460// read memory and see those traps, read from the MachTask
461// (m_task.ReadMemory()) as that version will give you what is actually
462// in inferior memory.
463//----------------------------------------------------------------------
464nub_size_t
465MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
466{
467 // We need to remove any current software traps (enabled software
468 // breakpoints) that we may have placed in our tasks memory.
469
470 // First just read the memory as is
471 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
472
473 // Then place any opcodes that fall into this range back into the buffer
474 // before we return this to callers.
475 if (bytes_read > 0)
476 RemoveTrapsFromBuffer (addr, size, (uint8_t *)buf);
477 return bytes_read;
478}
479
480//----------------------------------------------------------------------
481// WriteMemory from the MachProcess level will always write memory around
482// any software breakpoints. Any software breakpoints will have their
483// opcodes modified if they are enabled. Any memory that doesn't overlap
484// with software breakpoints will be written to. If you wish to write to
485// inferior memory without this interference, then write to the MachTask
486// (m_task.WriteMemory()) as that version will always modify inferior
487// memory.
488//----------------------------------------------------------------------
489nub_size_t
490MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
491{
492 // We need to write any data that would go where any current software traps
493 // (enabled software breakpoints) any software traps (breakpoints) that we
494 // may have placed in our tasks memory.
495
496 std::map<nub_addr_t, DNBBreakpoint *> addr_to_bp_map;
497 DNBBreakpoint *bp;
498 nub_size_t idx;
499 for (idx = 0; (bp = m_breakpoints.GetByIndex(idx)) != NULL; ++idx)
500 {
501 if (bp->IntersectsRange(addr, size, NULL, NULL, NULL))
502 addr_to_bp_map[bp->Address()] = bp;
503 }
504
505 // If we don't have any software breakpoints that are in this buffer, then
506 // we can just write memory and be done with it.
507 if (addr_to_bp_map.empty())
508 return m_task.WriteMemory(addr, size, buf);
509
510 // If we make it here, we have some breakpoints that overlap and we need
511 // to work around them.
512
513 nub_size_t bytes_written = 0;
514 nub_addr_t intersect_addr;
515 nub_size_t intersect_size;
516 nub_size_t opcode_offset;
517 const uint8_t *ubuf = (const uint8_t *)buf;
518 std::map<nub_addr_t, DNBBreakpoint *>::iterator pos, end = addr_to_bp_map.end();
519 for (pos = addr_to_bp_map.begin(); pos != end; ++pos)
520 {
521 bp = pos->second;
522
523 assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset));
524 assert(addr <= intersect_addr && intersect_addr < addr + size);
525 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
526 assert(opcode_offset + intersect_size <= bp->ByteSize());
527
528 // Check for bytes before this breakpoint
529 const nub_addr_t curr_addr = addr + bytes_written;
530 if (intersect_addr > curr_addr)
531 {
532 // There are some bytes before this breakpoint that we need to
533 // just write to memory
534 nub_size_t curr_size = intersect_addr - curr_addr;
535 nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
536 bytes_written += curr_bytes_written;
537 if (curr_bytes_written != curr_size)
538 {
539 // We weren't able to write all of the requested bytes, we
540 // are done looping and will return the number of bytes that
541 // we have written so far.
542 break;
543 }
544 }
545
546 // Now write any bytes that would cover up any software breakpoints
547 // directly into the breakpoint opcode buffer
548 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
549 bytes_written += intersect_size;
550 }
551
552 // Write any remaining bytes after the last breakpoint if we have any left
553 if (bytes_written < size)
554 bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written);
555
556 return bytes_written;
557}
558
559
560void
561MachProcess::ReplyToAllExceptions (const DNBThreadResumeActions& thread_actions)
562{
563 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
564 if (m_exception_messages.empty() == false)
565 {
566 MachException::Message::iterator pos;
567 MachException::Message::iterator begin = m_exception_messages.begin();
568 MachException::Message::iterator end = m_exception_messages.end();
569 for (pos = begin; pos != end; ++pos)
570 {
571 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %d...", std::distance(begin, pos));
572 int thread_reply_signal = 0;
573
574 const DNBThreadResumeAction *action = thread_actions.GetActionForThread (pos->state.thread_port, false);
575
576 if (action)
577 {
578 thread_reply_signal = action->signal;
579 if (thread_reply_signal)
580 thread_actions.SetSignalHandledForThread (pos->state.thread_port);
581 }
582
Greg Clayton3382c2c2010-07-30 23:14:42 +0000583 DNBError err (pos->Reply(this, thread_reply_signal));
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000584 if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
Greg Clayton3382c2c2010-07-30 23:14:42 +0000585 err.LogThreadedIfError("Error replying to exception");
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000586 }
587
588 // Erase all exception message as we should have used and replied
589 // to them all already.
590 m_exception_messages.clear();
591 }
592}
593void
594MachProcess::PrivateResume (const DNBThreadResumeActions& thread_actions)
595{
596 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
597
598 ReplyToAllExceptions (thread_actions);
599// bool stepOverBreakInstruction = step;
600
601 // Let the thread prepare to resume and see if any threads want us to
602 // step over a breakpoint instruction (ProcessWillResume will modify
603 // the value of stepOverBreakInstruction).
604 m_threadList.ProcessWillResume (this, thread_actions);
605
606 // Set our state accordingly
607 if (thread_actions.NumActionsWithState(eStateStepping))
608 SetState (eStateStepping);
609 else
610 SetState (eStateRunning);
611
612 // Now resume our task.
Greg Clayton3382c2c2010-07-30 23:14:42 +0000613 m_task.Resume();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000614}
615
616nub_break_t
617MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware, thread_t tid)
618{
619 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, hardware, tid);
620 if (hardware && tid == INVALID_NUB_THREAD)
621 tid = GetCurrentThread();
622
623 DNBBreakpoint bp(addr, length, tid, hardware);
624 nub_break_t breakID = m_breakpoints.Add(bp);
625 if (EnableBreakpoint(breakID))
626 {
627 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%4.4x ) => %u", (uint64_t)addr, length, tid, breakID);
628 return breakID;
629 }
630 else
631 {
632 m_breakpoints.Remove(breakID);
633 }
634 // We failed to enable the breakpoint
635 return INVALID_NUB_BREAK_ID;
636}
637
638nub_watch_t
639MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware, thread_t tid)
640{
641 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, flags = 0x%8.8x, hardware = %i, tid = 0x%4.4x )", (uint64_t)addr, length, watch_flags, hardware, tid);
642 if (hardware && tid == INVALID_NUB_THREAD)
643 tid = GetCurrentThread();
644
645 DNBBreakpoint watch(addr, length, tid, hardware);
646 watch.SetIsWatchpoint(watch_flags);
647
648 nub_watch_t watchID = m_watchpoints.Add(watch);
649 if (EnableWatchpoint(watchID))
650 {
651 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => %u", (uint64_t)addr, length, tid, watchID);
652 return watchID;
653 }
654 else
655 {
656 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %u, tid = 0x%x) => FAILED (%u)", (uint64_t)addr, length, tid, watchID);
657 m_watchpoints.Remove(watchID);
658 }
659 // We failed to enable the watchpoint
660 return INVALID_NUB_BREAK_ID;
661}
662
663nub_size_t
664MachProcess::DisableAllBreakpoints(bool remove)
665{
666 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
667 DNBBreakpoint *bp;
668 nub_size_t disabled_count = 0;
669 nub_size_t idx = 0;
670 while ((bp = m_breakpoints.GetByIndex(idx)) != NULL)
671 {
672 bool success = DisableBreakpoint(bp->GetID(), remove);
673
674 if (success)
675 disabled_count++;
676 // If we failed to disable the breakpoint or we aren't removing the breakpoint
677 // increment the breakpoint index. Otherwise DisableBreakpoint will have removed
678 // the breakpoint at this index and we don't need to change it.
679 if ((success == false) || (remove == false))
680 idx++;
681 }
682 return disabled_count;
683}
684
685nub_size_t
686MachProcess::DisableAllWatchpoints(bool remove)
687{
688 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove);
689 DNBBreakpoint *wp;
690 nub_size_t disabled_count = 0;
691 nub_size_t idx = 0;
692 while ((wp = m_watchpoints.GetByIndex(idx)) != NULL)
693 {
694 bool success = DisableWatchpoint(wp->GetID(), remove);
695
696 if (success)
697 disabled_count++;
698 // If we failed to disable the watchpoint or we aren't removing the watchpoint
699 // increment the watchpoint index. Otherwise DisableWatchpoint will have removed
700 // the watchpoint at this index and we don't need to change it.
701 if ((success == false) || (remove == false))
702 idx++;
703 }
704 return disabled_count;
705}
706
707bool
708MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove)
709{
710 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
711 if (bp)
712 {
713 nub_addr_t addr = bp->Address();
714 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr);
715
716 if (bp->IsHardware())
717 {
718 bool hw_disable_result = m_threadList.DisableHardwareBreakpoint (bp);
719
720 if (hw_disable_result == true)
721 {
722 bp->SetEnabled(false);
723 // Let the thread list know that a breakpoint has been modified
724 if (remove)
725 {
726 m_threadList.NotifyBreakpointChanged(bp);
727 m_breakpoints.Remove(breakID);
728 }
729 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", breakID, remove, (uint64_t)addr);
730 return true;
731 }
732
733 return false;
734 }
735
736 const nub_size_t break_op_size = bp->ByteSize();
737 assert (break_op_size > 0);
738 const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(bp->ByteSize());
739 if (break_op_size > 0)
740 {
741 // Clear a software breakoint instruction
742 uint8_t curr_break_op[break_op_size];
743 bool break_op_found = false;
744
745 // Read the breakpoint opcode
746 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size)
747 {
748 bool verify = false;
749 if (bp->IsEnabled())
750 {
751 // Make sure we have the a breakpoint opcode exists at this address
752 if (memcmp(curr_break_op, break_op, break_op_size) == 0)
753 {
754 break_op_found = true;
755 // We found a valid breakpoint opcode at this address, now restore
756 // the saved opcode.
757 if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
758 {
759 verify = true;
760 }
761 else
762 {
763 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx memory write failed when restoring original opcode", breakID, remove, (uint64_t)addr);
764 }
765 }
766 else
767 {
768 DNBLogWarning("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx expected a breakpoint opcode but didn't find one.", breakID, remove, (uint64_t)addr);
769 // Set verify to true and so we can check if the original opcode has already been restored
770 verify = true;
771 }
772 }
773 else
774 {
775 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x$8.8llx is not enabled", breakID, remove, (uint64_t)addr);
776 // Set verify to true and so we can check if the original opcode is there
777 verify = true;
778 }
779
780 if (verify)
781 {
782 uint8_t verify_opcode[break_op_size];
783 // Verify that our original opcode made it back to the inferior
784 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size)
785 {
786 // compare the memory we just read with the original opcode
787 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
788 {
789 // SUCCESS
790 bp->SetEnabled(false);
791 // Let the thread list know that a breakpoint has been modified
792 if (remove)
793 {
794 m_threadList.NotifyBreakpointChanged(bp);
795 m_breakpoints.Remove(breakID);
796 }
797 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx => success", breakID, remove, (uint64_t)addr);
798 return true;
799 }
800 else
801 {
802 if (break_op_found)
803 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: failed to restore original opcode", breakID, remove, (uint64_t)addr);
804 else
805 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx: opcode changed", breakID, remove, (uint64_t)addr);
806 }
807 }
808 else
809 {
810 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr);
811 }
812 }
813 }
814 else
815 {
816 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr);
817 }
818 }
819 }
820 else
821 {
822 DNBLogError("MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) invalid breakpoint ID", breakID, remove);
823 }
824 return false;
825}
826
827bool
828MachProcess::DisableWatchpoint(nub_watch_t watchID, bool remove)
829{
830 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(watchID = %d, remove = %d)", __FUNCTION__, watchID, remove);
831 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
832 if (wp)
833 {
834 nub_addr_t addr = wp->Address();
835 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx", watchID, remove, (uint64_t)addr);
836
837 if (wp->IsHardware())
838 {
839 bool hw_disable_result = m_threadList.DisableHardwareWatchpoint (wp);
840
841 if (hw_disable_result == true)
842 {
843 wp->SetEnabled(false);
844 if (remove)
845 m_watchpoints.Remove(watchID);
846 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( watchID = %d, remove = %d ) addr = 0x%8.8llx (hardware) => success", watchID, remove, (uint64_t)addr);
847 return true;
848 }
849 }
850
851 // TODO: clear software watchpoints if we implement them
852 }
853 else
854 {
855 DNBLogError("MachProcess::DisableWatchpoint ( watchID = %d, remove = %d ) invalid watchpoint ID", watchID, remove);
856 }
857 return false;
858}
859
860
861void
862MachProcess::DumpBreakpoint(nub_break_t breakID) const
863{
864 DNBLogThreaded("MachProcess::DumpBreakpoint(breakID = %d)", breakID);
865
866 if (NUB_BREAK_ID_IS_VALID(breakID))
867 {
868 const DNBBreakpoint *bp = m_breakpoints.FindByID(breakID);
869 if (bp)
870 bp->Dump();
871 else
872 DNBLog("MachProcess::DumpBreakpoint(breakID = %d): invalid breakID", breakID);
873 }
874 else
875 {
876 m_breakpoints.Dump();
877 }
878}
879
880void
881MachProcess::DumpWatchpoint(nub_watch_t watchID) const
882{
883 DNBLogThreaded("MachProcess::DumpWatchpoint(watchID = %d)", watchID);
884
885 if (NUB_BREAK_ID_IS_VALID(watchID))
886 {
887 const DNBBreakpoint *wp = m_watchpoints.FindByID(watchID);
888 if (wp)
889 wp->Dump();
890 else
891 DNBLog("MachProcess::DumpWatchpoint(watchID = %d): invalid watchID", watchID);
892 }
893 else
894 {
895 m_watchpoints.Dump();
896 }
897}
898
899bool
900MachProcess::EnableBreakpoint(nub_break_t breakID)
901{
902 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d )", breakID);
903 DNBBreakpoint *bp = m_breakpoints.FindByID (breakID);
904 if (bp)
905 {
906 nub_addr_t addr = bp->Address();
907 if (bp->IsEnabled())
908 {
909 DNBLogWarning("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint already enabled.", breakID, (uint64_t)addr);
910 return true;
911 }
912 else
913 {
914 if (bp->HardwarePreferred())
915 {
916 bp->SetHardwareIndex(m_threadList.EnableHardwareBreakpoint(bp));
917 if (bp->IsHardware())
918 {
919 bp->SetEnabled(true);
920 return true;
921 }
922 }
923
924 const nub_size_t break_op_size = bp->ByteSize();
925 assert (break_op_size != 0);
926 const uint8_t * const break_op = DNBArch::SoftwareBreakpointOpcode(break_op_size);
927 if (break_op_size > 0)
928 {
929 // Save the original opcode by reading it
930 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size)
931 {
932 // Write a software breakpoint in place of the original opcode
933 if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size)
934 {
935 uint8_t verify_break_op[4];
936 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size)
937 {
938 if (memcmp(break_op, verify_break_op, break_op_size) == 0)
939 {
940 bp->SetEnabled(true);
941 // Let the thread list know that a breakpoint has been modified
942 m_threadList.NotifyBreakpointChanged(bp);
943 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: SUCCESS.", breakID, (uint64_t)addr);
944 return true;
945 }
946 else
947 {
948 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: breakpoint opcode verification failed.", breakID, (uint64_t)addr);
949 }
950 }
951 else
952 {
953 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory to verify breakpoint opcode.", breakID, (uint64_t)addr);
954 }
955 }
956 else
957 {
958 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to write breakpoint opcode to memory.", breakID, (uint64_t)addr);
959 }
960 }
961 else
962 {
963 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) addr = 0x%8.8llx: unable to read memory at breakpoint address.", breakID, (uint64_t)addr);
964 }
965 }
966 else
967 {
968 DNBLogError("MachProcess::EnableBreakpoint ( breakID = %d ) no software breakpoint opcode for current architecture.", breakID);
969 }
970 }
971 }
972 return false;
973}
974
975bool
976MachProcess::EnableWatchpoint(nub_watch_t watchID)
977{
978 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(watchID = %d)", watchID);
979 DNBBreakpoint *wp = m_watchpoints.FindByID (watchID);
980 if (wp)
981 {
982 nub_addr_t addr = wp->Address();
983 if (wp->IsEnabled())
984 {
985 DNBLogWarning("MachProcess::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
986 return true;
987 }
988 else
989 {
990 // Currently only try and set hardware watchpoints.
991 wp->SetHardwareIndex(m_threadList.EnableHardwareWatchpoint(wp));
992 if (wp->IsHardware())
993 {
994 wp->SetEnabled(true);
995 return true;
996 }
997 // TODO: Add software watchpoints by doing page protection tricks.
998 }
999 }
1000 return false;
1001}
1002
1003// Called by the exception thread when an exception has been received from
1004// our process. The exception message is completely filled and the exception
1005// data has already been copied.
1006void
1007MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
1008{
1009 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1010
1011 if (m_exception_messages.empty())
1012 m_task.Suspend();
1013
1014 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
1015
1016 // Use a locker to automatically unlock our mutex in case of exceptions
1017 // Add the exception to our internal exception stack
1018 m_exception_messages.push_back(exceptionMessage);
1019}
1020
1021void
1022MachProcess::ExceptionMessageBundleComplete()
1023{
1024 // We have a complete bundle of exceptions for our child process.
1025 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex);
1026 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size());
1027 if (!m_exception_messages.empty())
1028 {
1029 // Let all threads recover from stopping and do any clean up based
1030 // on the previous thread state (if any).
1031 m_threadList.ProcessDidStop(this);
1032
1033 // Let each thread know of any exceptions
1034 task_t task = m_task.TaskPort();
1035 size_t i;
1036 for (i=0; i<m_exception_messages.size(); ++i)
1037 {
1038 // Let the thread list figure use the MachProcess to forward all exceptions
1039 // on down to each thread.
1040 if (m_exception_messages[i].state.task_port == task)
1041 m_threadList.NotifyException(m_exception_messages[i].state);
1042 if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
1043 m_exception_messages[i].Dump();
1044 }
1045
1046 if (DNBLogCheckLogBit(LOG_THREAD))
1047 m_threadList.Dump();
1048
1049 bool step_more = false;
1050 if (m_threadList.ShouldStop(step_more))
1051 {
1052 // Wait for the eEventProcessRunningStateChanged event to be reset
1053 // before changing state to stopped to avoid race condition with
1054 // very fast start/stops
1055 struct timespec timeout;
1056 //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms
1057 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
1058 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
1059 SetState(eStateStopped);
1060 }
1061 else
1062 {
1063 // Resume without checking our current state.
1064 PrivateResume (DNBThreadResumeActions (eStateRunning, 0));
1065 }
1066 }
1067 else
1068 {
1069 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size());
1070 }
1071}
1072
1073nub_size_t
1074MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed)
1075{
1076 if (m_image_infos_callback != NULL)
1077 return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton);
1078 return 0;
1079}
1080
1081void
1082MachProcess::SharedLibrariesUpdated ( )
1083{
1084 uint32_t event_bits = eEventSharedLibsStateChange;
1085 // Set the shared library event bit to let clients know of shared library
1086 // changes
1087 m_events.SetEvents(event_bits);
1088 // Wait for the event bit to reset if a reset ACK is requested
1089 m_events.WaitForResetAck(event_bits);
1090}
1091
1092void
1093MachProcess::AppendSTDOUT (char* s, size_t len)
1094{
1095 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%d> %s) ...", __FUNCTION__, len, s);
1096 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1097 m_stdout_data.append(s, len);
1098 m_events.SetEvents(eEventStdioAvailable);
1099
1100 // Wait for the event bit to reset if a reset ACK is requested
1101 m_events.WaitForResetAck(eEventStdioAvailable);
1102}
1103
1104size_t
1105MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size)
1106{
1107 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
1108 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex);
1109 size_t bytes_available = m_stdout_data.size();
1110 if (bytes_available > 0)
1111 {
1112 if (bytes_available > buf_size)
1113 {
1114 memcpy(buf, m_stdout_data.data(), buf_size);
1115 m_stdout_data.erase(0, buf_size);
1116 bytes_available = buf_size;
1117 }
1118 else
1119 {
1120 memcpy(buf, m_stdout_data.data(), bytes_available);
1121 m_stdout_data.clear();
1122 }
1123 }
1124 return bytes_available;
1125}
1126
1127nub_addr_t
1128MachProcess::GetDYLDAllImageInfosAddress ()
1129{
Greg Clayton3382c2c2010-07-30 23:14:42 +00001130 DNBError err;
1131 return m_task.GetDYLDAllImageInfosAddress(err);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001132}
1133
1134size_t
1135MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size)
1136{
1137 return 0;
1138}
1139
1140void *
1141MachProcess::STDIOThread(void *arg)
1142{
1143 MachProcess *proc = (MachProcess*) arg;
1144 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg);
1145
1146 // We start use a base and more options so we can control if we
1147 // are currently using a timeout on the mach_msg. We do this to get a
1148 // bunch of related exceptions on our exception port so we can process
1149 // then together. When we have multiple threads, we can get an exception
1150 // per thread and they will come in consecutively. The main thread loop
1151 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
1152 // flag set in the options, so we will wait forever for an exception on
1153 // our exception port. After we get one exception, we then will use the
1154 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
1155 // exceptions for our process. After we have received the last pending
1156 // exception, we will get a timeout which enables us to then notify
1157 // our main thread that we have an exception bundle avaiable. We then wait
1158 // for the main thread to tell this exception thread to start trying to get
1159 // exceptions messages again and we start again with a mach_msg read with
1160 // infinite timeout.
1161 DNBError err;
1162 int stdout_fd = proc->GetStdoutFileDescriptor();
1163 int stderr_fd = proc->GetStderrFileDescriptor();
1164 if (stdout_fd == stderr_fd)
1165 stderr_fd = -1;
1166
1167 while (stdout_fd >= 0 || stderr_fd >= 0)
1168 {
1169 ::pthread_testcancel ();
1170
1171 fd_set read_fds;
1172 FD_ZERO (&read_fds);
1173 if (stdout_fd >= 0)
1174 FD_SET (stdout_fd, &read_fds);
1175 if (stderr_fd >= 0)
1176 FD_SET (stderr_fd, &read_fds);
1177 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
1178
1179 int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
1180 DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1181
1182 if (num_set_fds < 0)
1183 {
1184 int select_errno = errno;
1185 if (DNBLogCheckLogBit(LOG_PROCESS))
1186 {
1187 err.SetError (select_errno, DNBError::POSIX);
1188 err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
1189 }
1190
1191 switch (select_errno)
1192 {
1193 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
1194 break;
1195 case EBADF: // One of the descriptor sets specified an invalid descriptor.
1196 return NULL;
1197 break;
1198 case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred.
1199 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
1200 default: // Other unknown error
1201 break;
1202 }
1203 }
1204 else if (num_set_fds == 0)
1205 {
1206 }
1207 else
1208 {
1209 char s[1024];
1210 s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination
1211 int bytes_read = 0;
1212 if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
1213 {
1214 do
1215 {
1216 bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
1217 if (bytes_read < 0)
1218 {
1219 int read_errno = errno;
1220 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1221 }
1222 else if (bytes_read == 0)
1223 {
1224 // EOF...
1225 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read);
1226 stdout_fd = -1;
1227 }
1228 else if (bytes_read > 0)
1229 {
1230 proc->AppendSTDOUT(s, bytes_read);
1231 }
1232
1233 } while (bytes_read > 0);
1234 }
1235
1236 if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
1237 {
1238 do
1239 {
1240 bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
1241 if (bytes_read < 0)
1242 {
1243 int read_errno = errno;
1244 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
1245 }
1246 else if (bytes_read == 0)
1247 {
1248 // EOF...
1249 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read);
1250 stderr_fd = -1;
1251 }
1252 else if (bytes_read > 0)
1253 {
1254 proc->AppendSTDOUT(s, bytes_read);
1255 }
1256
1257 } while (bytes_read > 0);
1258 }
1259 }
1260 }
1261 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg);
1262 return NULL;
1263}
1264
1265pid_t
1266MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len)
1267{
1268 // Clear out and clean up from any current state
1269 Clear();
1270 if (pid != 0)
1271 {
Greg Clayton3382c2c2010-07-30 23:14:42 +00001272 DNBError err;
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001273 // Make sure the process exists...
1274 if (::getpgid (pid) < 0)
1275 {
Greg Clayton3382c2c2010-07-30 23:14:42 +00001276 err.SetErrorToErrno();
1277 const char *err_cstr = err.AsString();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001278 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process");
1279 return INVALID_NUB_PROCESS;
1280 }
1281
1282 SetState(eStateAttaching);
1283 m_pid = pid;
1284 // Let ourselves know we are going to be using SBS if the correct flag bit is set...
1285#if defined (__arm__)
1286 if (IsSBProcess(pid))
1287 m_flags |= eMachProcessFlagsUsingSBS;
1288#endif
Greg Clayton3382c2c2010-07-30 23:14:42 +00001289 if (!m_task.StartExceptionThread(err))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001290 {
Greg Clayton3382c2c2010-07-30 23:14:42 +00001291 const char *err_cstr = err.AsString();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001292 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread");
1293 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
1294 m_pid = INVALID_NUB_PROCESS;
1295 return INVALID_NUB_PROCESS;
1296 }
1297
1298 errno = 0;
Greg Clayton3382c2c2010-07-30 23:14:42 +00001299 if (::ptrace (PT_ATTACHEXC, pid, 0, 0))
1300 err.SetError(errno);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001301 else
Greg Clayton3382c2c2010-07-30 23:14:42 +00001302 err.Clear();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001303
Greg Clayton3382c2c2010-07-30 23:14:42 +00001304 if (err.Success())
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001305 {
1306 m_flags |= eMachProcessFlagsAttached;
1307 // Sleep a bit to let the exception get received and set our process status
1308 // to stopped.
1309 ::usleep(250000);
1310 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
1311 return m_pid;
1312 }
1313 else
1314 {
Greg Clayton3382c2c2010-07-30 23:14:42 +00001315 ::snprintf (err_str, err_len, "%s", err.AsString());
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001316 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
1317 }
1318 }
1319 return INVALID_NUB_PROCESS;
1320}
1321
1322// Do the process specific setup for attach. If this returns NULL, then there's no
1323// platform specific stuff to be done to wait for the attach. If you get non-null,
1324// pass that token to the CheckForProcess method, and then to CleanupAfterAttach.
1325
1326// Call PrepareForAttach before attaching to a process that has not yet launched
1327// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach.
1328// You should call CleanupAfterAttach to free the token, and do whatever other
1329// cleanup seems good.
1330
1331const void *
1332MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &err_str)
1333{
1334#if defined (__arm__)
1335 // Tell SpringBoard to halt the next launch of this application on startup.
1336
1337 if (!waitfor)
1338 return NULL;
1339
1340 const char *app_ext = strstr(path, ".app");
1341 if (app_ext == NULL)
1342 {
1343 DNBLogThreadedIf(LOG_PROCESS, "%s: path '%s' doesn't contain .app, we can't tell springboard to wait for launch...", path);
1344 return NULL;
1345 }
1346
1347 if (launch_flavor != eLaunchFlavorSpringBoard
1348 && launch_flavor != eLaunchFlavorDefault)
1349 return NULL;
1350
1351 std::string app_bundle_path(path, app_ext + strlen(".app"));
1352
1353 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), err_str);
1354 std::string bundleIDStr;
1355 CFString::UTF8(bundleIDCFStr, bundleIDStr);
1356 DNBLogThreadedIf(LOG_PROCESS, "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", app_bundle_path.c_str (), bundleIDStr.c_str());
1357
1358 if (bundleIDCFStr == NULL)
1359 {
1360 return NULL;
1361 }
1362
1363 SBSApplicationLaunchError sbs_error = 0;
1364
1365 const char *stdout_err = "/dev/null";
1366 CFString stdio_path;
1367 stdio_path.SetFileSystemRepresentation (stdout_err);
1368
1369 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", bundleIDStr.c_str(), stdout_err, stdout_err);
1370 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1371 (CFURLRef)NULL, // openURL
1372 NULL, // launch_argv.get(),
1373 NULL, // launch_envp.get(), // CFDictionaryRef environment
1374 stdio_path.get(),
1375 stdio_path.get(),
1376 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
1377
1378 if (sbs_error != SBSApplicationLaunchErrorSuccess)
1379 {
1380 err_str.SetError(sbs_error, DNBError::SpringBoard);
1381 return NULL;
1382 }
1383
1384 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
1385 return bundleIDCFStr;
1386# else
1387 return NULL;
1388#endif
1389}
1390
1391// Pass in the token you got from PrepareForAttach. If there is a process
1392// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
1393// will be returned.
1394
1395nub_process_t
1396MachProcess::CheckForProcess (const void *attach_token)
1397{
1398 if (attach_token == NULL)
1399 return INVALID_NUB_PROCESS;
1400
1401#if defined (__arm__)
1402 CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
1403 Boolean got_it;
1404 nub_process_t attach_pid;
1405 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
1406 if (got_it)
1407 return attach_pid;
1408 else
1409 return INVALID_NUB_PROCESS;
1410#endif
1411 return INVALID_NUB_PROCESS;
1412}
1413
1414// Call this to clean up after you have either attached or given up on the attach.
1415// Pass true for success if you have attached, false if you have not.
1416// The token will also be freed at this point, so you can't use it after calling
1417// this method.
1418
1419void
1420MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str)
1421{
1422#if defined (__arm__)
1423 if (attach_token == NULL)
1424 return;
1425
1426 // Tell SpringBoard to cancel the debug on next launch of this application
1427 // if we failed to attach
1428 if (!success)
1429 {
1430 SBSApplicationLaunchError sbs_error = 0;
1431 CFStringRef bundleIDCFStr = (CFStringRef) attach_token;
1432
1433 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1434 (CFURLRef)NULL,
1435 NULL,
1436 NULL,
1437 NULL,
1438 NULL,
1439 SBSApplicationCancelDebugOnNextLaunch);
1440
1441 if (sbs_error != SBSApplicationLaunchErrorSuccess)
1442 {
1443 err_str.SetError(sbs_error, DNBError::SpringBoard);
1444 return;
1445 }
1446 }
1447
1448 CFRelease((CFStringRef) attach_token);
1449#endif
1450}
1451
1452pid_t
1453MachProcess::LaunchForDebug
1454(
1455 const char *path,
1456 char const *argv[],
1457 char const *envp[],
1458 const char *stdio_path,
1459 nub_launch_flavor_t launch_flavor,
1460 DNBError &launch_err
1461)
1462{
1463 // Clear out and clean up from any current state
1464 Clear();
1465
1466 DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u )", __FUNCTION__, path, argv, envp, launch_flavor);
1467
1468 // Fork a child process for debugging
1469 SetState(eStateLaunching);
1470
1471 switch (launch_flavor)
1472 {
1473 case eLaunchFlavorForkExec:
1474 m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err);
1475 break;
1476
1477 case eLaunchFlavorPosixSpawn:
1478 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, argv, envp, stdio_path, this, launch_err);
1479 break;
1480
1481#if defined (__arm__)
1482
1483 case eLaunchFlavorSpringBoard:
1484 {
1485 const char *app_ext = strstr(path, ".app");
1486 if (app_ext != NULL)
1487 {
1488 std::string app_bundle_path(path, app_ext + strlen(".app"));
1489 return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, launch_err);
1490 }
1491 }
1492 break;
1493
1494#endif
1495
1496 default:
1497 // Invalid launch
1498 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1499 return INVALID_NUB_PROCESS;
1500 }
1501
1502 if (m_pid == INVALID_NUB_PROCESS)
1503 {
1504 // If we don't have a valid process ID and no one has set the error,
1505 // then return a generic error
1506 if (launch_err.Success())
1507 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1508 }
1509 else
1510 {
1511 m_path = path;
1512 size_t i;
1513 char const *arg;
1514 for (i=0; (arg = argv[i]) != NULL; i++)
1515 m_args.push_back(arg);
1516
Greg Clayton3382c2c2010-07-30 23:14:42 +00001517 m_task.StartExceptionThread(launch_err);
1518 if (launch_err.Fail())
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001519 {
Greg Clayton3382c2c2010-07-30 23:14:42 +00001520 if (launch_err.AsString() == NULL)
1521 launch_err.SetErrorString("unable to start the exception thread");
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001522 ::ptrace (PT_KILL, m_pid, 0, 0);
1523 m_pid = INVALID_NUB_PROCESS;
1524 return INVALID_NUB_PROCESS;
1525 }
1526
1527 StartSTDIOThread();
1528
1529 if (launch_flavor == eLaunchFlavorPosixSpawn)
1530 {
1531
1532 SetState (eStateAttaching);
1533 errno = 0;
1534 int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
1535 if (err == 0)
1536 {
1537 m_flags |= eMachProcessFlagsAttached;
1538 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
1539 launch_err.Clear();
1540 }
1541 else
1542 {
1543 SetState (eStateExited);
1544 DNBError ptrace_err(errno, DNBError::POSIX);
1545 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid %d (err = %i, errno = %i (%s))", m_pid, err, ptrace_err.Error(), ptrace_err.AsString());
1546 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
1547 }
1548 }
1549 else
1550 {
1551 launch_err.Clear();
1552 }
1553 }
1554 return m_pid;
1555}
1556
1557pid_t
1558MachProcess::PosixSpawnChildForPTraceDebugging
1559(
1560 const char *path,
1561 char const *argv[],
1562 char const *envp[],
1563 const char *stdio_path,
1564 MachProcess* process,
1565 DNBError& err
1566)
1567{
1568 posix_spawnattr_t attr;
1569 DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp);
1570
1571 err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX);
1572 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1573 err.LogThreaded("::posix_spawnattr_init ( &attr )");
1574 if (err.Fail())
1575 return INVALID_NUB_PROCESS;
1576
1577 err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), DNBError::POSIX);
1578 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1579 err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )");
1580 if (err.Fail())
1581 return INVALID_NUB_PROCESS;
1582
1583 // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
1584 // and we will fail to continue with our process...
1585
1586 // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
1587
1588//#ifndef _POSIX_SPAWN_DISABLE_ASLR
1589//#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
1590//#endif
1591// err.SetError( ::posix_spawnattr_setflags (&attr, _POSIX_SPAWN_DISABLE_ASLR), DNBError::POSIX);
1592// if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1593// err.LogThreaded("::posix_spawnattr_setflags ( &attr, _POSIX_SPAWN_DISABLE_ASLR )");
1594
1595#if !defined(__arm__)
1596
1597 // We don't need to do this for ARM, and we really shouldn't now that we
1598 // have multiple CPU subtypes and no posix_spawnattr call that allows us
1599 // to set which CPU subtype to launch...
1600 cpu_type_t cpu_type = DNBArch::GetCPUType();
1601 size_t ocount = 0;
1602 err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX);
1603 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1604 err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu_type, ocount);
1605
1606 if (err.Fail() != 0 || ocount != 1)
1607 return INVALID_NUB_PROCESS;
1608
1609#endif
1610
1611 PseudoTerminal pty;
1612
1613 posix_spawn_file_actions_t file_actions;
1614 err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX);
1615 int file_actions_valid = err.Success();
1616 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
1617 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
1618 int pty_error = -1;
1619 pid_t pid = INVALID_NUB_PROCESS;
1620 if (file_actions_valid)
1621 {
1622 if (stdio_path == NULL)
1623 {
1624 pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
1625 if (pty_error == PseudoTerminal::success)
1626 stdio_path = pty.SlaveName();
1627 // Make sure we were able to get the slave name
1628 if (stdio_path == NULL)
1629 stdio_path = "/dev/null";
1630 }
1631
1632 if (stdio_path != NULL)
1633 {
1634 err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stdio_path, O_RDWR, 0), DNBError::POSIX);
1635 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1636 err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stdio_path);
1637
1638 err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdio_path, O_RDONLY, 0), DNBError::POSIX);
1639 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1640 err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdio_path);
1641
1642 err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdio_path, O_WRONLY, 0), DNBError::POSIX);
1643 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1644 err.LogThreaded("::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdio_path);
1645 }
1646 err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
1647 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1648 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
1649 }
1650 else
1651 {
1652 err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX);
1653 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1654 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
1655 }
1656
1657 // We have seen some cases where posix_spawnp was returning a valid
1658 // looking pid even when an error was returned, so clear it out
1659 if (err.Fail())
1660 pid = INVALID_NUB_PROCESS;
1661
1662 if (pty_error == 0)
1663 {
1664 if (process != NULL)
1665 {
1666 int master_fd = pty.ReleaseMasterFD();
1667 process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
1668 }
1669 }
Greg Clayton0b42ac32010-07-02 01:29:13 +00001670
1671 ::posix_spawnattr_destroy (&attr);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001672
1673 if (file_actions_valid)
1674 {
1675 DNBError err2;
1676 err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX);
1677 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
1678 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
1679 }
1680
1681 return pid;
1682}
1683
1684pid_t
1685MachProcess::ForkChildForPTraceDebugging
1686(
1687 const char *path,
1688 char const *argv[],
1689 char const *envp[],
1690 MachProcess* process,
1691 DNBError& launch_err
1692)
1693{
1694 PseudoTerminal::Error pty_error = PseudoTerminal::success;
1695
1696 // Use a fork that ties the child process's stdin/out/err to a pseudo
1697 // terminal so we can read it in our MachProcess::STDIOThread
1698 // as unbuffered io.
1699 PseudoTerminal pty;
1700 pid_t pid = pty.Fork(pty_error);
1701
1702 if (pid < 0)
1703 {
1704 //--------------------------------------------------------------
1705 // Error during fork.
1706 //--------------------------------------------------------------
1707 return pid;
1708 }
1709 else if (pid == 0)
1710 {
1711 //--------------------------------------------------------------
1712 // Child process
1713 //--------------------------------------------------------------
1714 ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process
1715 ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
1716
1717 // If our parent is setgid, lets make sure we don't inherit those
1718 // extra powers due to nepotism.
1719 ::setgid (getgid ());
1720
1721 // Let the child have its own process group. We need to execute
1722 // this call in both the child and parent to avoid a race condition
1723 // between the two processes.
1724 ::setpgid (0, 0); // Set the child process group to match its pid
1725
1726 // Sleep a bit to before the exec call
1727 ::sleep (1);
1728
1729 // Turn this process into
1730 ::execv (path, (char * const *)argv);
1731 // Exit with error code. Child process should have taken
1732 // over in above exec call and if the exec fails it will
1733 // exit the child process below.
1734 ::exit (127);
1735 }
1736 else
1737 {
1738 //--------------------------------------------------------------
1739 // Parent process
1740 //--------------------------------------------------------------
1741 // Let the child have its own process group. We need to execute
1742 // this call in both the child and parent to avoid a race condition
1743 // between the two processes.
1744 ::setpgid (pid, pid); // Set the child process group to match its pid
1745
1746 if (process != NULL)
1747 {
1748 // Release our master pty file descriptor so the pty class doesn't
1749 // close it and so we can continue to use it in our STDIO thread
1750 int master_fd = pty.ReleaseMasterFD();
1751 process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
1752 }
1753 }
1754 return pid;
1755}
1756
1757#if defined (__arm__)
1758
1759pid_t
1760MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], DNBError &launch_err)
1761{
1762 // Clear out and clean up from any current state
1763 Clear();
1764
1765 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
1766
1767 // Fork a child process for debugging
1768 SetState(eStateLaunching);
1769 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, this, launch_err);
1770 if (m_pid != 0)
1771 {
1772 m_flags |= eMachProcessFlagsUsingSBS;
1773 m_path = path;
1774 size_t i;
1775 char const *arg;
1776 for (i=0; (arg = argv[i]) != NULL; i++)
1777 m_args.push_back(arg);
1778 m_task.StartExceptionThread();
1779 StartSTDIOThread();
1780 SetState (eStateAttaching);
1781 int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
1782 if (err == 0)
1783 {
1784 m_flags |= eMachProcessFlagsAttached;
1785 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
1786 }
1787 else
1788 {
1789 SetState (eStateExited);
1790 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
1791 }
1792 }
1793 return m_pid;
1794}
1795
1796#include <servers/bootstrap.h>
1797
1798// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
1799// or NULL if there was some problem getting the bundle id.
1800static CFStringRef
1801CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str)
1802{
1803 CFBundle bundle(app_bundle_path);
1804 CFStringRef bundleIDCFStr = bundle.GetIdentifier();
1805 std::string bundleID;
1806 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
1807 {
1808 struct stat app_bundle_stat;
1809 char err_msg[PATH_MAX];
1810
1811 if (::stat (app_bundle_path, &app_bundle_stat) < 0)
1812 {
1813 err_str.SetError(errno, DNBError::POSIX);
1814 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path);
1815 err_str.SetErrorString(err_msg);
1816 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
1817 }
1818 else
1819 {
1820 err_str.SetError(-1, DNBError::Generic);
1821 snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path);
1822 err_str.SetErrorString(err_msg);
1823 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
1824 }
1825 return NULL;
1826 }
1827
1828 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
1829 CFRetain (bundleIDCFStr);
1830
1831 return bundleIDCFStr;
1832}
1833
1834pid_t
1835MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], MachProcess* process, DNBError &launch_err)
1836{
1837 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
1838 CFAllocatorRef alloc = kCFAllocatorDefault;
1839
1840 if (argv[0] == NULL)
1841 return INVALID_NUB_PROCESS;
1842
1843 size_t argc = 0;
1844 // Count the number of arguments
1845 while (argv[argc] != NULL)
1846 argc++;
1847
1848 // Enumerate the arguments
1849 size_t first_launch_arg_idx = 1;
1850 CFReleaser<CFMutableArrayRef> launch_argv;
1851
1852 if (argv[first_launch_arg_idx])
1853 {
1854 size_t launch_argc = argc > 0 ? argc - 1 : 0;
1855 launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
1856 size_t i;
1857 char const *arg;
1858 CFString launch_arg;
1859 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
1860 {
1861 launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
1862 if (launch_arg.get() != NULL)
1863 CFArrayAppendValue(launch_argv.get(), launch_arg.get());
1864 else
1865 break;
1866 }
1867 }
1868
1869 // Next fill in the arguments dictionary. Note, the envp array is of the form
1870 // Variable=value but SpringBoard wants a CF dictionary. So we have to convert
1871 // this here.
1872
1873 CFReleaser<CFMutableDictionaryRef> launch_envp;
1874
1875 if (envp[0])
1876 {
1877 launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1878 const char *value;
1879 int name_len;
1880 CFString name_string, value_string;
1881
1882 for (int i = 0; envp[i] != NULL; i++)
1883 {
1884 value = strstr (envp[i], "=");
1885
1886 // If the name field is empty or there's no =, skip it. Somebody's messing with us.
1887 if (value == NULL || value == envp[i])
1888 continue;
1889
1890 name_len = value - envp[i];
1891
1892 // Now move value over the "="
1893 value++;
1894
1895 name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
1896 value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
1897 CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
1898 }
1899 }
1900
1901 CFString stdio_path;
1902
1903 PseudoTerminal pty;
1904 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
1905 if (pty_err == PseudoTerminal::success)
1906 {
1907 const char* slave_name = pty.SlaveName();
1908 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
1909 if (slave_name && slave_name[0])
1910 {
1911 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
1912 stdio_path.SetFileSystemRepresentation (slave_name);
1913 }
1914 }
1915
1916 if (stdio_path.get() == NULL)
1917 {
1918 stdio_path.SetFileSystemRepresentation ("/dev/null");
1919 }
1920
1921 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err);
1922 if (bundleIDCFStr == NULL)
1923 return INVALID_NUB_PROCESS;
1924
1925 std::string bundleID;
1926 CFString::UTF8(bundleIDCFStr, bundleID);
1927
1928 CFData argv_data(NULL);
1929
1930 if (launch_argv.get())
1931 {
1932 if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL)
1933 {
1934 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__);
1935 return INVALID_NUB_PROCESS;
1936 }
1937 }
1938
1939 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
1940
1941 // Find SpringBoard
1942 SBSApplicationLaunchError sbs_error = 0;
1943 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr,
1944 (CFURLRef)NULL, // openURL
1945 launch_argv.get(),
1946 launch_envp.get(), // CFDictionaryRef environment
1947 stdio_path.get(),
1948 stdio_path.get(),
1949 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
1950
1951
1952 launch_err.SetError(sbs_error, DNBError::SpringBoard);
1953
1954 if (sbs_error == SBSApplicationLaunchErrorSuccess)
1955 {
1956 static const useconds_t pid_poll_interval = 200000;
1957 static const useconds_t pid_poll_timeout = 30000000;
1958
1959 useconds_t pid_poll_total = 0;
1960
1961 nub_process_t pid = INVALID_NUB_PROCESS;
1962 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
1963 // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
1964 // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
1965 // yet, or that it died very quickly (if you weren't using waitForDebugger).
1966 while (!pid_found && pid_poll_total < pid_poll_timeout)
1967 {
1968 usleep (pid_poll_interval);
1969 pid_poll_total += pid_poll_interval;
1970 DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
1971 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
1972 }
1973
1974 CFRelease (bundleIDCFStr);
1975 if (pid_found)
1976 {
1977 if (process != NULL)
1978 {
1979 // Release our master pty file descriptor so the pty class doesn't
1980 // close it and so we can continue to use it in our STDIO thread
1981 int master_fd = pty.ReleaseMasterFD();
1982 process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
1983 }
1984 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
1985 }
1986 else
1987 {
1988 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
1989 }
1990 return pid;
1991 }
1992
1993 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
1994 return INVALID_NUB_PROCESS;
1995}
1996
1997#endif // #if defined (__arm__)
1998
1999