1//===-- 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 <inttypes.h> 16#include <mach/mach.h> 17#include <signal.h> 18#include <spawn.h> 19#include <sys/fcntl.h> 20#include <sys/types.h> 21#include <sys/ptrace.h> 22#include <sys/stat.h> 23#include <sys/sysctl.h> 24#include <unistd.h> 25#include <pthread.h> 26#include "MacOSX/CFUtils.h" 27#include "SysSignal.h" 28 29#include <algorithm> 30#include <map> 31 32#include "DNBDataRef.h" 33#include "DNBLog.h" 34#include "DNBThreadResumeActions.h" 35#include "DNBTimer.h" 36#include "MachProcess.h" 37#include "PseudoTerminal.h" 38 39#include "CFBundle.h" 40#include "CFData.h" 41#include "CFString.h" 42 43static CFStringRef CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str); 44 45#ifdef WITH_SPRINGBOARD 46 47#include <CoreFoundation/CoreFoundation.h> 48#include <SpringBoardServices/SpringBoardServer.h> 49#include <SpringBoardServices/SBSWatchdogAssertion.h> 50 51static bool 52IsSBProcess (nub_process_t pid) 53{ 54 CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); 55 return appIdsForPID.get() != NULL; 56} 57 58#endif // WITH_SPRINGBOARD 59 60#ifdef WITH_BKS 61#import <Foundation/Foundation.h> 62extern "C" 63{ 64#import <BackBoardServices/BackBoardServices.h> 65#import <BackBoardServices/BKSSystemService_LaunchServices.h> 66#import <BackBoardServices/BKSOpenApplicationConstants_Private.h> 67} 68 69static bool 70IsBKSProcess (nub_process_t pid) 71{ 72 BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init]; 73 BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid]; 74 return app_state != BKSApplicationStateUnknown; 75} 76 77static void 78SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error) 79{ 80 error.SetError (error_code, DNBError::BackBoard); 81 NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(error_code); 82 const char *err_str = NULL; 83 if (err_nsstr == NULL) 84 err_str = "unknown BKS error"; 85 else 86 { 87 err_str = [err_nsstr UTF8String]; 88 if (err_str == NULL) 89 err_str = "unknown BKS error"; 90 } 91 error.SetErrorString(err_str); 92} 93 94static const int BKS_OPEN_APPLICATION_TIMEOUT_ERROR = 111; 95#endif // WITH_BKS 96#if 0 97#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) 98#else 99#define DEBUG_LOG(fmt, ...) 100#endif 101 102#ifndef MACH_PROCESS_USE_POSIX_SPAWN 103#define MACH_PROCESS_USE_POSIX_SPAWN 1 104#endif 105 106#ifndef _POSIX_SPAWN_DISABLE_ASLR 107#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 108#endif 109 110MachProcess::MachProcess() : 111 m_pid (0), 112 m_cpu_type (0), 113 m_child_stdin (-1), 114 m_child_stdout (-1), 115 m_child_stderr (-1), 116 m_path (), 117 m_args (), 118 m_task (this), 119 m_flags (eMachProcessFlagsNone), 120 m_stdio_thread (0), 121 m_stdio_mutex (PTHREAD_MUTEX_RECURSIVE), 122 m_stdout_data (), 123 m_thread_actions (), 124 m_profile_enabled (false), 125 m_profile_interval_usec (0), 126 m_profile_thread (0), 127 m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), 128 m_profile_data (), 129 m_thread_list (), 130 m_activities (), 131 m_exception_messages (), 132 m_exception_messages_mutex (PTHREAD_MUTEX_RECURSIVE), 133 m_state (eStateUnloaded), 134 m_state_mutex (PTHREAD_MUTEX_RECURSIVE), 135 m_events (0, kAllEventsMask), 136 m_private_events (0, kAllEventsMask), 137 m_breakpoints (), 138 m_watchpoints (), 139 m_name_to_addr_callback(NULL), 140 m_name_to_addr_baton(NULL), 141 m_image_infos_callback(NULL), 142 m_image_infos_baton(NULL), 143 m_sent_interrupt_signo (0), 144 m_auto_resume_signo (0), 145 m_did_exec (false) 146{ 147 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 148} 149 150MachProcess::~MachProcess() 151{ 152 DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); 153 Clear(); 154} 155 156pid_t 157MachProcess::SetProcessID(pid_t pid) 158{ 159 // Free any previous process specific data or resources 160 Clear(); 161 // Set the current PID appropriately 162 if (pid == 0) 163 m_pid = ::getpid (); 164 else 165 m_pid = pid; 166 return m_pid; // Return actually PID in case a zero pid was passed in 167} 168 169nub_state_t 170MachProcess::GetState() 171{ 172 // If any other threads access this we will need a mutex for it 173 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 174 return m_state; 175} 176 177const char * 178MachProcess::ThreadGetName(nub_thread_t tid) 179{ 180 return m_thread_list.GetName(tid); 181} 182 183nub_state_t 184MachProcess::ThreadGetState(nub_thread_t tid) 185{ 186 return m_thread_list.GetState(tid); 187} 188 189 190nub_size_t 191MachProcess::GetNumThreads () const 192{ 193 return m_thread_list.NumThreads(); 194} 195 196nub_thread_t 197MachProcess::GetThreadAtIndex (nub_size_t thread_idx) const 198{ 199 return m_thread_list.ThreadIDAtIndex(thread_idx); 200} 201 202nub_thread_t 203MachProcess::GetThreadIDForMachPortNumber (thread_t mach_port_number) const 204{ 205 return m_thread_list.GetThreadIDByMachPortNumber (mach_port_number); 206} 207 208nub_bool_t 209MachProcess::SyncThreadState (nub_thread_t tid) 210{ 211 MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid)); 212 if (!thread_sp) 213 return false; 214 kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber()); 215 DNBLogThreadedIf (LOG_THREAD, "thread = 0x%8.8" PRIx32 " calling thread_abort_safely (tid) => %u (GetGPRState() for stop_count = %u)", thread_sp->MachPortNumber(), kret, thread_sp->Process()->StopCount()); 216 217 if (kret == KERN_SUCCESS) 218 return true; 219 else 220 return false; 221 222} 223 224ThreadInfo::QoS 225MachProcess::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index) 226{ 227 return m_thread_list.GetRequestedQoS (tid, tsd, dti_qos_class_index); 228} 229 230nub_addr_t 231MachProcess::GetPThreadT (nub_thread_t tid) 232{ 233 return m_thread_list.GetPThreadT (tid); 234} 235 236nub_addr_t 237MachProcess::GetDispatchQueueT (nub_thread_t tid) 238{ 239 return m_thread_list.GetDispatchQueueT (tid); 240} 241 242nub_addr_t 243MachProcess::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) 244{ 245 return m_thread_list.GetTSDAddressForThread (tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size); 246} 247 248nub_thread_t 249MachProcess::GetCurrentThread () 250{ 251 return m_thread_list.CurrentThreadID(); 252} 253 254nub_thread_t 255MachProcess::GetCurrentThreadMachPort () 256{ 257 return m_thread_list.GetMachPortNumberByThreadID(m_thread_list.CurrentThreadID()); 258} 259 260nub_thread_t 261MachProcess::SetCurrentThread(nub_thread_t tid) 262{ 263 return m_thread_list.SetCurrentThread(tid); 264} 265 266bool 267MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) 268{ 269 if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) 270 { 271 if (m_did_exec) 272 stop_info->reason = eStopTypeExec; 273 return true; 274 } 275 return false; 276} 277 278void 279MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const 280{ 281 return m_thread_list.DumpThreadStoppedReason(tid); 282} 283 284const char * 285MachProcess::GetThreadInfo(nub_thread_t tid) const 286{ 287 return m_thread_list.GetThreadInfo(tid); 288} 289 290uint32_t 291MachProcess::GetCPUType () 292{ 293 if (m_cpu_type == 0 && m_pid != 0) 294 m_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); 295 return m_cpu_type; 296} 297 298const DNBRegisterSetInfo * 299MachProcess::GetRegisterSetInfo (nub_thread_t tid, nub_size_t *num_reg_sets) const 300{ 301 MachThreadSP thread_sp (m_thread_list.GetThreadByID (tid)); 302 if (thread_sp) 303 { 304 DNBArchProtocol *arch = thread_sp->GetArchProtocol(); 305 if (arch) 306 return arch->GetRegisterSetInfo (num_reg_sets); 307 } 308 *num_reg_sets = 0; 309 return NULL; 310} 311 312bool 313MachProcess::GetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *value ) const 314{ 315 return m_thread_list.GetRegisterValue(tid, set, reg, value); 316} 317 318bool 319MachProcess::SetRegisterValue ( nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *value ) const 320{ 321 return m_thread_list.SetRegisterValue(tid, set, reg, value); 322} 323 324void 325MachProcess::SetState(nub_state_t new_state) 326{ 327 // If any other threads access this we will need a mutex for it 328 uint32_t event_mask = 0; 329 330 // Scope for mutex locker 331 { 332 PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); 333 const nub_state_t old_state = m_state; 334 335 if (old_state == eStateExited) 336 { 337 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new state since current state is exited", DNBStateAsString(new_state)); 338 } 339 else if (old_state == new_state) 340 { 341 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring redundant state change...", DNBStateAsString(new_state)); 342 } 343 else 344 { 345 if (NUB_STATE_IS_STOPPED(new_state)) 346 event_mask = eEventProcessStoppedStateChanged; 347 else 348 event_mask = eEventProcessRunningStateChanged; 349 350 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous state was %s), event_mask = 0x%8.8x", DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask); 351 352 m_state = new_state; 353 if (new_state == eStateStopped) 354 m_stop_count++; 355 } 356 } 357 358 if (event_mask != 0) 359 { 360 m_events.SetEvents (event_mask); 361 m_private_events.SetEvents (event_mask); 362 if (event_mask == eEventProcessStoppedStateChanged) 363 m_private_events.ResetEvents (eEventProcessRunningStateChanged); 364 else 365 m_private_events.ResetEvents (eEventProcessStoppedStateChanged); 366 367 // Wait for the event bit to reset if a reset ACK is requested 368 m_events.WaitForResetAck(event_mask); 369 } 370 371} 372 373void 374MachProcess::Clear(bool detaching) 375{ 376 // Clear any cached thread list while the pid and task are still valid 377 378 m_task.Clear(); 379 // Now clear out all member variables 380 m_pid = INVALID_NUB_PROCESS; 381 if (!detaching) 382 CloseChildFileDescriptors(); 383 384 m_path.clear(); 385 m_args.clear(); 386 SetState(eStateUnloaded); 387 m_flags = eMachProcessFlagsNone; 388 m_stop_count = 0; 389 m_thread_list.Clear(); 390 { 391 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 392 m_exception_messages.clear(); 393 } 394 m_activities.Clear(); 395 if (m_profile_thread) 396 { 397 pthread_join(m_profile_thread, NULL); 398 m_profile_thread = NULL; 399 } 400} 401 402 403bool 404MachProcess::StartSTDIOThread() 405{ 406 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 407 // Create the thread that watches for the child STDIO 408 return ::pthread_create (&m_stdio_thread, NULL, MachProcess::STDIOThread, this) == 0; 409} 410 411void 412MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) 413{ 414 m_profile_enabled = enable; 415 m_profile_interval_usec = interval_usec; 416 m_profile_scan_type = scan_type; 417 418 if (m_profile_enabled && (m_profile_thread == NULL)) 419 { 420 StartProfileThread(); 421 } 422 else if (!m_profile_enabled && m_profile_thread) 423 { 424 pthread_join(m_profile_thread, NULL); 425 m_profile_thread = NULL; 426 } 427} 428 429bool 430MachProcess::StartProfileThread() 431{ 432 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__); 433 // Create the thread that profiles the inferior and reports back if enabled 434 return ::pthread_create (&m_profile_thread, NULL, MachProcess::ProfileThread, this) == 0; 435} 436 437 438nub_addr_t 439MachProcess::LookupSymbol(const char *name, const char *shlib) 440{ 441 if (m_name_to_addr_callback != NULL && name && name[0]) 442 return m_name_to_addr_callback(ProcessID(), name, shlib, m_name_to_addr_baton); 443 return INVALID_NUB_ADDRESS; 444} 445 446bool 447MachProcess::Resume (const DNBThreadResumeActions& thread_actions) 448{ 449 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()"); 450 nub_state_t state = GetState(); 451 452 if (CanResume(state)) 453 { 454 m_thread_actions = thread_actions; 455 PrivateResume(); 456 return true; 457 } 458 else if (state == eStateRunning) 459 { 460 DNBLog("Resume() - task 0x%x is already running, ignoring...", m_task.TaskPort()); 461 return true; 462 } 463 DNBLog("Resume() - task 0x%x has state %s, can't continue...", m_task.TaskPort(), DNBStateAsString(state)); 464 return false; 465} 466 467bool 468MachProcess::Kill (const struct timespec *timeout_abstime) 469{ 470 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()"); 471 nub_state_t state = DoSIGSTOP(true, false, NULL); 472 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s", DNBStateAsString(state)); 473 errno = 0; 474 DNBLog ("Sending ptrace PT_KILL to terminate inferior process."); 475 ::ptrace (PT_KILL, m_pid, 0, 0); 476 DNBError err; 477 err.SetErrorToErrno(); 478 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() ::ptrace (PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)", m_pid, err.Error(), err.AsString()); 479 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 480 PrivateResume (); 481 482 // Try and reap the process without touching our m_events since 483 // we want the code above this to still get the eStateExited event 484 const uint32_t reap_timeout_usec = 1000000; // Wait 1 second and try to reap the process 485 const uint32_t reap_interval_usec = 10000; // 486 uint32_t reap_time_elapsed; 487 for (reap_time_elapsed = 0; 488 reap_time_elapsed < reap_timeout_usec; 489 reap_time_elapsed += reap_interval_usec) 490 { 491 if (GetState() == eStateExited) 492 break; 493 usleep(reap_interval_usec); 494 } 495 DNBLog ("Waited %u ms for process to be reaped (state = %s)", reap_time_elapsed/1000, DNBStateAsString(GetState())); 496 return true; 497} 498 499bool 500MachProcess::Interrupt() 501{ 502 nub_state_t state = GetState(); 503 if (IsRunning(state)) 504 { 505 if (m_sent_interrupt_signo == 0) 506 { 507 m_sent_interrupt_signo = SIGSTOP; 508 if (Signal (m_sent_interrupt_signo)) 509 { 510 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - sent %i signal to interrupt process", m_sent_interrupt_signo); 511 } 512 else 513 { 514 m_sent_interrupt_signo = 0; 515 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to send %i signal to interrupt process", m_sent_interrupt_signo); 516 } 517 } 518 else 519 { 520 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously sent an interrupt signal %i that hasn't been received yet, interrupt aborted", m_sent_interrupt_signo); 521 } 522 } 523 else 524 { 525 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already stopped, no interrupt sent"); 526 } 527 return false; 528} 529 530bool 531MachProcess::Signal (int signal, const struct timespec *timeout_abstime) 532{ 533 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p)", signal, timeout_abstime); 534 nub_state_t state = GetState(); 535 if (::kill (ProcessID(), signal) == 0) 536 { 537 // If we were running and we have a timeout, wait for the signal to stop 538 if (IsRunning(state) && timeout_abstime) 539 { 540 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); 541 m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); 542 state = GetState(); 543 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); 544 return !IsRunning (state); 545 } 546 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) not waiting...", signal, timeout_abstime); 547 return true; 548 } 549 DNBError err(errno, DNBError::POSIX); 550 err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal); 551 return false; 552 553} 554 555bool 556MachProcess::SendEvent (const char *event, DNBError &send_err) 557{ 558 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid); 559 if (m_pid == INVALID_NUB_PROCESS) 560 return false; 561#if WITH_BKS 562 return BKSSendEvent (event, send_err); 563#endif 564 return true; 565} 566 567nub_state_t 568MachProcess::DoSIGSTOP (bool clear_bps_and_wps, bool allow_running, uint32_t *thread_idx_ptr) 569{ 570 nub_state_t state = GetState(); 571 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s", DNBStateAsString (state)); 572 573 if (!IsRunning(state)) 574 { 575 if (clear_bps_and_wps) 576 { 577 DisableAllBreakpoints (true); 578 DisableAllWatchpoints (true); 579 clear_bps_and_wps = false; 580 } 581 582 // If we already have a thread stopped due to a SIGSTOP, we don't have 583 // to do anything... 584 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 585 if (thread_idx_ptr) 586 *thread_idx_ptr = thread_idx; 587 if (thread_idx != UINT32_MAX) 588 return GetState(); 589 590 // No threads were stopped with a SIGSTOP, we need to run and halt the 591 // process with a signal 592 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- resuming process", DNBStateAsString (state)); 593 if (allow_running) 594 m_thread_actions = DNBThreadResumeActions (eStateRunning, 0); 595 else 596 m_thread_actions = DNBThreadResumeActions (eStateSuspended, 0); 597 598 PrivateResume (); 599 600 // Reset the event that says we were indeed running 601 m_events.ResetEvents(eEventProcessRunningStateChanged); 602 state = GetState(); 603 } 604 605 // We need to be stopped in order to be able to detach, so we need 606 // to send ourselves a SIGSTOP 607 608 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP", DNBStateAsString (state)); 609 struct timespec sigstop_timeout; 610 DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0); 611 Signal (SIGSTOP, &sigstop_timeout); 612 if (clear_bps_and_wps) 613 { 614 DisableAllBreakpoints (true); 615 DisableAllWatchpoints (true); 616 //clear_bps_and_wps = false; 617 } 618 uint32_t thread_idx = m_thread_list.GetThreadIndexForThreadStoppedWithSignal (SIGSTOP); 619 if (thread_idx_ptr) 620 *thread_idx_ptr = thread_idx; 621 return GetState(); 622} 623 624bool 625MachProcess::Detach() 626{ 627 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()"); 628 629 uint32_t thread_idx = UINT32_MAX; 630 nub_state_t state = DoSIGSTOP(true, true, &thread_idx); 631 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s", DNBStateAsString(state)); 632 633 { 634 m_thread_actions.Clear(); 635 m_activities.Clear(); 636 DNBThreadResumeAction thread_action; 637 thread_action.tid = m_thread_list.ThreadIDAtIndex (thread_idx); 638 thread_action.state = eStateRunning; 639 thread_action.signal = -1; 640 thread_action.addr = INVALID_NUB_ADDRESS; 641 642 m_thread_actions.Append (thread_action); 643 m_thread_actions.SetDefaultThreadActionIfNeeded (eStateRunning, 0); 644 645 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 646 647 ReplyToAllExceptions (); 648 649 } 650 651 m_task.ShutDownExcecptionThread(); 652 653 // Detach from our process 654 errno = 0; 655 nub_process_t pid = m_pid; 656 int ret = ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); 657 DNBError err(errno, DNBError::POSIX); 658 if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0)) 659 err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); 660 661 // Resume our task 662 m_task.Resume(); 663 664 // NULL our task out as we have already retored all exception ports 665 m_task.Clear(); 666 667 // Clear out any notion of the process we once were 668 const bool detaching = true; 669 Clear(detaching); 670 671 SetState(eStateDetached); 672 673 return true; 674} 675 676//---------------------------------------------------------------------- 677// ReadMemory from the MachProcess level will always remove any software 678// breakpoints from the memory buffer before returning. If you wish to 679// read memory and see those traps, read from the MachTask 680// (m_task.ReadMemory()) as that version will give you what is actually 681// in inferior memory. 682//---------------------------------------------------------------------- 683nub_size_t 684MachProcess::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf) 685{ 686 // We need to remove any current software traps (enabled software 687 // breakpoints) that we may have placed in our tasks memory. 688 689 // First just read the memory as is 690 nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf); 691 692 // Then place any opcodes that fall into this range back into the buffer 693 // before we return this to callers. 694 if (bytes_read > 0) 695 m_breakpoints.RemoveTrapsFromBuffer (addr, bytes_read, buf); 696 return bytes_read; 697} 698 699//---------------------------------------------------------------------- 700// WriteMemory from the MachProcess level will always write memory around 701// any software breakpoints. Any software breakpoints will have their 702// opcodes modified if they are enabled. Any memory that doesn't overlap 703// with software breakpoints will be written to. If you wish to write to 704// inferior memory without this interference, then write to the MachTask 705// (m_task.WriteMemory()) as that version will always modify inferior 706// memory. 707//---------------------------------------------------------------------- 708nub_size_t 709MachProcess::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) 710{ 711 // We need to write any data that would go where any current software traps 712 // (enabled software breakpoints) any software traps (breakpoints) that we 713 // may have placed in our tasks memory. 714 715 std::vector<DNBBreakpoint *> bps; 716 717 const size_t num_bps = m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps); 718 if (num_bps == 0) 719 return m_task.WriteMemory(addr, size, buf); 720 721 nub_size_t bytes_written = 0; 722 nub_addr_t intersect_addr; 723 nub_size_t intersect_size; 724 nub_size_t opcode_offset; 725 const uint8_t *ubuf = (const uint8_t *)buf; 726 727 for (size_t i=0; i<num_bps; ++i) 728 { 729 DNBBreakpoint *bp = bps[i]; 730 731 const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset); 732 assert(intersects); 733 assert(addr <= intersect_addr && intersect_addr < addr + size); 734 assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); 735 assert(opcode_offset + intersect_size <= bp->ByteSize()); 736 737 // Check for bytes before this breakpoint 738 const nub_addr_t curr_addr = addr + bytes_written; 739 if (intersect_addr > curr_addr) 740 { 741 // There are some bytes before this breakpoint that we need to 742 // just write to memory 743 nub_size_t curr_size = intersect_addr - curr_addr; 744 nub_size_t curr_bytes_written = m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written); 745 bytes_written += curr_bytes_written; 746 if (curr_bytes_written != curr_size) 747 { 748 // We weren't able to write all of the requested bytes, we 749 // are done looping and will return the number of bytes that 750 // we have written so far. 751 break; 752 } 753 } 754 755 // Now write any bytes that would cover up any software breakpoints 756 // directly into the breakpoint opcode buffer 757 ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); 758 bytes_written += intersect_size; 759 } 760 761 // Write any remaining bytes after the last breakpoint if we have any left 762 if (bytes_written < size) 763 bytes_written += m_task.WriteMemory(addr + bytes_written, size - bytes_written, ubuf + bytes_written); 764 765 return bytes_written; 766} 767 768void 769MachProcess::ReplyToAllExceptions () 770{ 771 PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex); 772 if (m_exception_messages.empty() == false) 773 { 774 MachException::Message::iterator pos; 775 MachException::Message::iterator begin = m_exception_messages.begin(); 776 MachException::Message::iterator end = m_exception_messages.end(); 777 for (pos = begin; pos != end; ++pos) 778 { 779 DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...", (uint32_t)std::distance(begin, pos)); 780 int thread_reply_signal = 0; 781 782 nub_thread_t tid = m_thread_list.GetThreadIDByMachPortNumber (pos->state.thread_port); 783 const DNBThreadResumeAction *action = NULL; 784 if (tid != INVALID_NUB_THREAD) 785 { 786 action = m_thread_actions.GetActionForThread (tid, false); 787 } 788 789 if (action) 790 { 791 thread_reply_signal = action->signal; 792 if (thread_reply_signal) 793 m_thread_actions.SetSignalHandledForThread (tid); 794 } 795 796 DNBError err (pos->Reply(this, thread_reply_signal)); 797 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 798 err.LogThreadedIfError("Error replying to exception"); 799 } 800 801 // Erase all exception message as we should have used and replied 802 // to them all already. 803 m_exception_messages.clear(); 804 } 805} 806void 807MachProcess::PrivateResume () 808{ 809 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 810 811 m_auto_resume_signo = m_sent_interrupt_signo; 812 if (m_auto_resume_signo) 813 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming (with unhandled interrupt signal %i)...", m_task.TaskPort(), m_auto_resume_signo); 814 else 815 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x resuming...", m_task.TaskPort()); 816 817 ReplyToAllExceptions (); 818// bool stepOverBreakInstruction = step; 819 820 // Let the thread prepare to resume and see if any threads want us to 821 // step over a breakpoint instruction (ProcessWillResume will modify 822 // the value of stepOverBreakInstruction). 823 m_thread_list.ProcessWillResume (this, m_thread_actions); 824 825 // Set our state accordingly 826 if (m_thread_actions.NumActionsWithState(eStateStepping)) 827 SetState (eStateStepping); 828 else 829 SetState (eStateRunning); 830 831 // Now resume our task. 832 m_task.Resume(); 833} 834 835DNBBreakpoint * 836MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware) 837{ 838 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu, hardware = %i)", (uint64_t)addr, (uint64_t)length, hardware); 839 840 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 841 if (bp) 842 bp->Retain(); 843 else 844 bp = m_breakpoints.Add(addr, length, hardware); 845 846 if (EnableBreakpoint(addr)) 847 { 848 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, bp); 849 return bp; 850 } 851 else if (bp->Release() == 0) 852 { 853 m_breakpoints.Remove(addr); 854 } 855 // We failed to enable the breakpoint 856 return NULL; 857} 858 859DNBBreakpoint * 860MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length, uint32_t watch_flags, bool hardware) 861{ 862 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu, flags = 0x%8.8x, hardware = %i)", (uint64_t)addr, (uint64_t)length, watch_flags, hardware); 863 864 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 865 // since the Z packets only send an address, we can only have one watchpoint at 866 // an address. If there is already one, we must refuse to create another watchpoint 867 if (wp) 868 return NULL; 869 870 wp = m_watchpoints.Add(addr, length, hardware); 871 wp->SetIsWatchpoint(watch_flags); 872 873 if (EnableWatchpoint(addr)) 874 { 875 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => %p", (uint64_t)addr, (uint64_t)length, wp); 876 return wp; 877 } 878 else 879 { 880 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = 0x%8.8llx, length = %llu) => FAILED", (uint64_t)addr, (uint64_t)length); 881 m_watchpoints.Remove(addr); 882 } 883 // We failed to enable the watchpoint 884 return NULL; 885} 886 887void 888MachProcess::DisableAllBreakpoints (bool remove) 889{ 890 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 891 892 m_breakpoints.DisableAllBreakpoints (this); 893 894 if (remove) 895 m_breakpoints.RemoveDisabled(); 896} 897 898void 899MachProcess::DisableAllWatchpoints(bool remove) 900{ 901 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )", __FUNCTION__, remove); 902 903 m_watchpoints.DisableAllWatchpoints(this); 904 905 if (remove) 906 m_watchpoints.RemoveDisabled(); 907} 908 909bool 910MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) 911{ 912 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 913 if (bp) 914 { 915 // After "exec" we might end up with a bunch of breakpoints that were disabled 916 // manually, just ignore them 917 if (!bp->IsEnabled()) 918 { 919 // Breakpoint might have been disabled by an exec 920 if (remove && bp->Release() == 0) 921 { 922 m_thread_list.NotifyBreakpointChanged(bp); 923 m_breakpoints.Remove(addr); 924 } 925 return true; 926 } 927 928 // We have multiple references to this breakpoint, decrement the ref count 929 // and if it isn't zero, then return true; 930 if (remove && bp->Release() > 0) 931 return true; 932 933 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); 934 935 if (bp->IsHardware()) 936 { 937 bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint (bp); 938 939 if (hw_disable_result == true) 940 { 941 bp->SetEnabled(false); 942 // Let the thread list know that a breakpoint has been modified 943 if (remove) 944 { 945 m_thread_list.NotifyBreakpointChanged(bp); 946 m_breakpoints.Remove(addr); 947 } 948 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); 949 return true; 950 } 951 952 return false; 953 } 954 955 const nub_size_t break_op_size = bp->ByteSize(); 956 assert (break_op_size > 0); 957 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (bp->ByteSize()); 958 if (break_op_size > 0) 959 { 960 // Clear a software breakpoint instruction 961 uint8_t curr_break_op[break_op_size]; 962 bool break_op_found = false; 963 964 // Read the breakpoint opcode 965 if (m_task.ReadMemory(addr, break_op_size, curr_break_op) == break_op_size) 966 { 967 bool verify = false; 968 if (bp->IsEnabled()) 969 { 970 // Make sure we have the a breakpoint opcode exists at this address 971 if (memcmp(curr_break_op, break_op, break_op_size) == 0) 972 { 973 break_op_found = true; 974 // We found a valid breakpoint opcode at this address, now restore 975 // the saved opcode. 976 if (m_task.WriteMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 977 { 978 verify = true; 979 } 980 else 981 { 982 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) memory write failed when restoring original opcode", (uint64_t)addr, remove); 983 } 984 } 985 else 986 { 987 DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) expected a breakpoint opcode but didn't find one.", (uint64_t)addr, remove); 988 // Set verify to true and so we can check if the original opcode has already been restored 989 verify = true; 990 } 991 } 992 else 993 { 994 DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) is not enabled", (uint64_t)addr, remove); 995 // Set verify to true and so we can check if the original opcode is there 996 verify = true; 997 } 998 999 if (verify) 1000 { 1001 uint8_t verify_opcode[break_op_size]; 1002 // Verify that our original opcode made it back to the inferior 1003 if (m_task.ReadMemory(addr, break_op_size, verify_opcode) == break_op_size) 1004 { 1005 // compare the memory we just read with the original opcode 1006 if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) == 0) 1007 { 1008 // SUCCESS 1009 bp->SetEnabled(false); 1010 // Let the thread list know that a breakpoint has been modified 1011 if (remove && bp->Release() == 0) 1012 { 1013 m_thread_list.NotifyBreakpointChanged(bp); 1014 m_breakpoints.Remove(addr); 1015 } 1016 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) => success", (uint64_t)addr, remove); 1017 return true; 1018 } 1019 else 1020 { 1021 if (break_op_found) 1022 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : failed to restore original opcode", (uint64_t)addr, remove); 1023 else 1024 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) : opcode changed", (uint64_t)addr, remove); 1025 } 1026 } 1027 else 1028 { 1029 DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable breakpoint 0x%8.8llx", (uint64_t)addr); 1030 } 1031 } 1032 } 1033 else 1034 { 1035 DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory at 0x%8.8llx", (uint64_t)addr); 1036 } 1037 } 1038 } 1039 else 1040 { 1041 DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d ) invalid breakpoint address", (uint64_t)addr, remove); 1042 } 1043 return false; 1044} 1045 1046bool 1047MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) 1048{ 1049 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s(addr = 0x%8.8llx, remove = %d)", __FUNCTION__, (uint64_t)addr, remove); 1050 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 1051 if (wp) 1052 { 1053 // If we have multiple references to a watchpoint, removing the watchpoint shouldn't clear it 1054 if (remove && wp->Release() > 0) 1055 return true; 1056 1057 nub_addr_t addr = wp->Address(); 1058 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )", (uint64_t)addr, remove); 1059 1060 if (wp->IsHardware()) 1061 { 1062 bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint (wp); 1063 1064 if (hw_disable_result == true) 1065 { 1066 wp->SetEnabled(false); 1067 if (remove) 1068 m_watchpoints.Remove(addr); 1069 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( addr = 0x%8.8llx, remove = %d ) (hardware) => success", (uint64_t)addr, remove); 1070 return true; 1071 } 1072 } 1073 1074 // TODO: clear software watchpoints if we implement them 1075 } 1076 else 1077 { 1078 DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d ) invalid watchpoint ID", (uint64_t)addr, remove); 1079 } 1080 return false; 1081} 1082 1083 1084uint32_t 1085MachProcess::GetNumSupportedHardwareWatchpoints () const 1086{ 1087 return m_thread_list.NumSupportedHardwareWatchpoints(); 1088} 1089 1090bool 1091MachProcess::EnableBreakpoint(nub_addr_t addr) 1092{ 1093 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )", (uint64_t)addr); 1094 DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); 1095 if (bp) 1096 { 1097 if (bp->IsEnabled()) 1098 { 1099 DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint already enabled.", (uint64_t)addr); 1100 return true; 1101 } 1102 else 1103 { 1104 if (bp->HardwarePreferred()) 1105 { 1106 bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); 1107 if (bp->IsHardware()) 1108 { 1109 bp->SetEnabled(true); 1110 return true; 1111 } 1112 } 1113 1114 const nub_size_t break_op_size = bp->ByteSize(); 1115 assert (break_op_size != 0); 1116 const uint8_t * const break_op = DNBArchProtocol::GetBreakpointOpcode (break_op_size); 1117 if (break_op_size > 0) 1118 { 1119 // Save the original opcode by reading it 1120 if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) == break_op_size) 1121 { 1122 // Write a software breakpoint in place of the original opcode 1123 if (m_task.WriteMemory(addr, break_op_size, break_op) == break_op_size) 1124 { 1125 uint8_t verify_break_op[4]; 1126 if (m_task.ReadMemory(addr, break_op_size, verify_break_op) == break_op_size) 1127 { 1128 if (memcmp(break_op, verify_break_op, break_op_size) == 0) 1129 { 1130 bp->SetEnabled(true); 1131 // Let the thread list know that a breakpoint has been modified 1132 m_thread_list.NotifyBreakpointChanged(bp); 1133 DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) : SUCCESS.", (uint64_t)addr); 1134 return true; 1135 } 1136 else 1137 { 1138 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): breakpoint opcode verification failed.", (uint64_t)addr); 1139 } 1140 } 1141 else 1142 { 1143 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory to verify breakpoint opcode.", (uint64_t)addr); 1144 } 1145 } 1146 else 1147 { 1148 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to write breakpoint opcode to memory.", (uint64_t)addr); 1149 } 1150 } 1151 else 1152 { 1153 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): unable to read memory at breakpoint address.", (uint64_t)addr); 1154 } 1155 } 1156 else 1157 { 1158 DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no software breakpoint opcode for current architecture.", (uint64_t)addr); 1159 } 1160 } 1161 } 1162 return false; 1163} 1164 1165bool 1166MachProcess::EnableWatchpoint(nub_addr_t addr) 1167{ 1168 DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)", (uint64_t)addr); 1169 DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr); 1170 if (wp) 1171 { 1172 nub_addr_t addr = wp->Address(); 1173 if (wp->IsEnabled()) 1174 { 1175 DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): watchpoint already enabled.", (uint64_t)addr); 1176 return true; 1177 } 1178 else 1179 { 1180 // Currently only try and set hardware watchpoints. 1181 wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp)); 1182 if (wp->IsHardware()) 1183 { 1184 wp->SetEnabled(true); 1185 return true; 1186 } 1187 // TODO: Add software watchpoints by doing page protection tricks. 1188 } 1189 } 1190 return false; 1191} 1192 1193// Called by the exception thread when an exception has been received from 1194// our process. The exception message is completely filled and the exception 1195// data has already been copied. 1196void 1197MachProcess::ExceptionMessageReceived (const MachException::Message& exceptionMessage) 1198{ 1199 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1200 1201 if (m_exception_messages.empty()) 1202 m_task.Suspend(); 1203 1204 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )"); 1205 1206 // Use a locker to automatically unlock our mutex in case of exceptions 1207 // Add the exception to our internal exception stack 1208 m_exception_messages.push_back(exceptionMessage); 1209} 1210 1211task_t 1212MachProcess::ExceptionMessageBundleComplete() 1213{ 1214 // We have a complete bundle of exceptions for our child process. 1215 PTHREAD_MUTEX_LOCKER (locker, m_exception_messages_mutex); 1216 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 1217 bool auto_resume = false; 1218 if (!m_exception_messages.empty()) 1219 { 1220 m_did_exec = false; 1221 // First check for any SIGTRAP and make sure we didn't exec 1222 const task_t task = m_task.TaskPort(); 1223 size_t i; 1224 if (m_pid != 0) 1225 { 1226 bool received_interrupt = false; 1227 uint32_t num_task_exceptions = 0; 1228 for (i=0; i<m_exception_messages.size(); ++i) 1229 { 1230 if (m_exception_messages[i].state.task_port == task) 1231 { 1232 ++num_task_exceptions; 1233 const int signo = m_exception_messages[i].state.SoftSignal(); 1234 if (signo == SIGTRAP) 1235 { 1236 // SIGTRAP could mean that we exec'ed. We need to check the 1237 // dyld all_image_infos.infoArray to see if it is NULL and if 1238 // so, say that we exec'ed. 1239 const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress(); 1240 if (aii_addr != INVALID_NUB_ADDRESS) 1241 { 1242 const nub_addr_t info_array_count_addr = aii_addr + 4; 1243 uint32_t info_array_count = 0; 1244 if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4) 1245 { 1246 if (info_array_count == 0) 1247 { 1248 m_did_exec = true; 1249 // Force the task port to update itself in case the task port changed after exec 1250 DNBError err; 1251 const task_t old_task = m_task.TaskPort(); 1252 const task_t new_task = m_task.TaskPortForProcessID (err, true); 1253 if (old_task != new_task) 1254 DNBLogThreadedIf(LOG_PROCESS, "exec: task changed from 0x%4.4x to 0x%4.4x", old_task, new_task); 1255 } 1256 } 1257 else 1258 { 1259 DNBLog ("error: failed to read all_image_infos.infoArrayCount from 0x%8.8llx", (uint64_t)info_array_count_addr); 1260 } 1261 } 1262 break; 1263 } 1264 else if (m_sent_interrupt_signo != 0 && signo == m_sent_interrupt_signo) 1265 { 1266 received_interrupt = true; 1267 } 1268 } 1269 } 1270 1271 if (m_did_exec) 1272 { 1273 cpu_type_t process_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); 1274 if (m_cpu_type != process_cpu_type) 1275 { 1276 DNBLog ("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, process_cpu_type); 1277 m_cpu_type = process_cpu_type; 1278 DNBArchProtocol::SetArchitecture (process_cpu_type); 1279 } 1280 m_thread_list.Clear(); 1281 m_activities.Clear(); 1282 m_breakpoints.DisableAll(); 1283 } 1284 1285 if (m_sent_interrupt_signo != 0) 1286 { 1287 if (received_interrupt) 1288 { 1289 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): process successfully interrupted with signal %i", m_sent_interrupt_signo); 1290 1291 // Mark that we received the interrupt signal 1292 m_sent_interrupt_signo = 0; 1293 // Not check if we had a case where: 1294 // 1 - We called MachProcess::Interrupt() but we stopped for another reason 1295 // 2 - We called MachProcess::Resume() (but still haven't gotten the interrupt signal) 1296 // 3 - We are now incorrectly stopped because we are handling the interrupt signal we missed 1297 // 4 - We might need to resume if we stopped only with the interrupt signal that we never handled 1298 if (m_auto_resume_signo != 0) 1299 { 1300 // Only auto_resume if we stopped with _only_ the interrupt signal 1301 if (num_task_exceptions == 1) 1302 { 1303 auto_resume = true; 1304 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): auto resuming due to unhandled interrupt signal %i", m_auto_resume_signo); 1305 } 1306 m_auto_resume_signo = 0; 1307 } 1308 } 1309 else 1310 { 1311 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::ExceptionMessageBundleComplete(): didn't get signal %i after MachProcess::Interrupt()", 1312 m_sent_interrupt_signo); 1313 } 1314 } 1315 } 1316 1317 // Let all threads recover from stopping and do any clean up based 1318 // on the previous thread state (if any). 1319 m_thread_list.ProcessDidStop(this); 1320 m_activities.Clear(); 1321 1322 // Let each thread know of any exceptions 1323 for (i=0; i<m_exception_messages.size(); ++i) 1324 { 1325 // Let the thread list figure use the MachProcess to forward all exceptions 1326 // on down to each thread. 1327 if (m_exception_messages[i].state.task_port == task) 1328 m_thread_list.NotifyException(m_exception_messages[i].state); 1329 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 1330 m_exception_messages[i].Dump(); 1331 } 1332 1333 if (DNBLogCheckLogBit(LOG_THREAD)) 1334 m_thread_list.Dump(); 1335 1336 bool step_more = false; 1337 if (m_thread_list.ShouldStop(step_more) && auto_resume == false) 1338 { 1339 // Wait for the eEventProcessRunningStateChanged event to be reset 1340 // before changing state to stopped to avoid race condition with 1341 // very fast start/stops 1342 struct timespec timeout; 1343 //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms 1344 DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms 1345 m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout); 1346 SetState(eStateStopped); 1347 } 1348 else 1349 { 1350 // Resume without checking our current state. 1351 PrivateResume (); 1352 } 1353 } 1354 else 1355 { 1356 DNBLogThreadedIf(LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); 1357 } 1358 return m_task.TaskPort(); 1359} 1360 1361nub_size_t 1362MachProcess::CopyImageInfos ( struct DNBExecutableImageInfo **image_infos, bool only_changed) 1363{ 1364 if (m_image_infos_callback != NULL) 1365 return m_image_infos_callback(ProcessID(), image_infos, only_changed, m_image_infos_baton); 1366 return 0; 1367} 1368 1369void 1370MachProcess::SharedLibrariesUpdated ( ) 1371{ 1372 uint32_t event_bits = eEventSharedLibsStateChange; 1373 // Set the shared library event bit to let clients know of shared library 1374 // changes 1375 m_events.SetEvents(event_bits); 1376 // Wait for the event bit to reset if a reset ACK is requested 1377 m_events.WaitForResetAck(event_bits); 1378} 1379 1380void 1381MachProcess::SetExitInfo (const char *info) 1382{ 1383 if (info && info[0]) 1384 { 1385 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__, info); 1386 m_exit_info.assign(info); 1387 } 1388 else 1389 { 1390 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__); 1391 m_exit_info.clear(); 1392 } 1393} 1394 1395void 1396MachProcess::AppendSTDOUT (char* s, size_t len) 1397{ 1398 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__, (uint64_t)len, s); 1399 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1400 m_stdout_data.append(s, len); 1401 m_events.SetEvents(eEventStdioAvailable); 1402 1403 // Wait for the event bit to reset if a reset ACK is requested 1404 m_events.WaitForResetAck(eEventStdioAvailable); 1405} 1406 1407size_t 1408MachProcess::GetAvailableSTDOUT (char *buf, size_t buf_size) 1409{ 1410 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size); 1411 PTHREAD_MUTEX_LOCKER (locker, m_stdio_mutex); 1412 size_t bytes_available = m_stdout_data.size(); 1413 if (bytes_available > 0) 1414 { 1415 if (bytes_available > buf_size) 1416 { 1417 memcpy(buf, m_stdout_data.data(), buf_size); 1418 m_stdout_data.erase(0, buf_size); 1419 bytes_available = buf_size; 1420 } 1421 else 1422 { 1423 memcpy(buf, m_stdout_data.data(), bytes_available); 1424 m_stdout_data.clear(); 1425 } 1426 } 1427 return bytes_available; 1428} 1429 1430nub_addr_t 1431MachProcess::GetDYLDAllImageInfosAddress () 1432{ 1433 DNBError err; 1434 return m_task.GetDYLDAllImageInfosAddress(err); 1435} 1436 1437size_t 1438MachProcess::GetAvailableSTDERR (char *buf, size_t buf_size) 1439{ 1440 return 0; 1441} 1442 1443void * 1444MachProcess::STDIOThread(void *arg) 1445{ 1446 MachProcess *proc = (MachProcess*) arg; 1447 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1448 1449#if defined (__APPLE__) 1450 pthread_setname_np ("stdio monitoring thread"); 1451#endif 1452 1453 // We start use a base and more options so we can control if we 1454 // are currently using a timeout on the mach_msg. We do this to get a 1455 // bunch of related exceptions on our exception port so we can process 1456 // then together. When we have multiple threads, we can get an exception 1457 // per thread and they will come in consecutively. The main thread loop 1458 // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT 1459 // flag set in the options, so we will wait forever for an exception on 1460 // our exception port. After we get one exception, we then will use the 1461 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current 1462 // exceptions for our process. After we have received the last pending 1463 // exception, we will get a timeout which enables us to then notify 1464 // our main thread that we have an exception bundle available. We then wait 1465 // for the main thread to tell this exception thread to start trying to get 1466 // exceptions messages again and we start again with a mach_msg read with 1467 // infinite timeout. 1468 DNBError err; 1469 int stdout_fd = proc->GetStdoutFileDescriptor(); 1470 int stderr_fd = proc->GetStderrFileDescriptor(); 1471 if (stdout_fd == stderr_fd) 1472 stderr_fd = -1; 1473 1474 while (stdout_fd >= 0 || stderr_fd >= 0) 1475 { 1476 ::pthread_testcancel (); 1477 1478 fd_set read_fds; 1479 FD_ZERO (&read_fds); 1480 if (stdout_fd >= 0) 1481 FD_SET (stdout_fd, &read_fds); 1482 if (stderr_fd >= 0) 1483 FD_SET (stderr_fd, &read_fds); 1484 int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; 1485 1486 int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); 1487 DNBLogThreadedIf(LOG_PROCESS, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1488 1489 if (num_set_fds < 0) 1490 { 1491 int select_errno = errno; 1492 if (DNBLogCheckLogBit(LOG_PROCESS)) 1493 { 1494 err.SetError (select_errno, DNBError::POSIX); 1495 err.LogThreadedIfError("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); 1496 } 1497 1498 switch (select_errno) 1499 { 1500 case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO 1501 break; 1502 case EBADF: // One of the descriptor sets specified an invalid descriptor. 1503 return NULL; 1504 break; 1505 case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. 1506 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. 1507 default: // Other unknown error 1508 break; 1509 } 1510 } 1511 else if (num_set_fds == 0) 1512 { 1513 } 1514 else 1515 { 1516 char s[1024]; 1517 s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination 1518 int bytes_read = 0; 1519 if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) 1520 { 1521 do 1522 { 1523 bytes_read = ::read (stdout_fd, s, sizeof(s)-1); 1524 if (bytes_read < 0) 1525 { 1526 int read_errno = errno; 1527 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1528 } 1529 else if (bytes_read == 0) 1530 { 1531 // EOF... 1532 DNBLogThreadedIf(LOG_PROCESS, "read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); 1533 stdout_fd = -1; 1534 } 1535 else if (bytes_read > 0) 1536 { 1537 proc->AppendSTDOUT(s, bytes_read); 1538 } 1539 1540 } while (bytes_read > 0); 1541 } 1542 1543 if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) 1544 { 1545 do 1546 { 1547 bytes_read = ::read (stderr_fd, s, sizeof(s)-1); 1548 if (bytes_read < 0) 1549 { 1550 int read_errno = errno; 1551 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); 1552 } 1553 else if (bytes_read == 0) 1554 { 1555 // EOF... 1556 DNBLogThreadedIf(LOG_PROCESS, "read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); 1557 stderr_fd = -1; 1558 } 1559 else if (bytes_read > 0) 1560 { 1561 proc->AppendSTDOUT(s, bytes_read); 1562 } 1563 1564 } while (bytes_read > 0); 1565 } 1566 } 1567 } 1568 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...", __FUNCTION__, arg); 1569 return NULL; 1570} 1571 1572 1573void 1574MachProcess::SignalAsyncProfileData (const char *info) 1575{ 1576 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info); 1577 PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); 1578 m_profile_data.push_back(info); 1579 m_events.SetEvents(eEventProfileDataAvailable); 1580 1581 // Wait for the event bit to reset if a reset ACK is requested 1582 m_events.WaitForResetAck(eEventProfileDataAvailable); 1583} 1584 1585 1586size_t 1587MachProcess::GetAsyncProfileData (char *buf, size_t buf_size) 1588{ 1589 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__, buf, (uint64_t)buf_size); 1590 PTHREAD_MUTEX_LOCKER (locker, m_profile_data_mutex); 1591 if (m_profile_data.empty()) 1592 return 0; 1593 1594 size_t bytes_available = m_profile_data.front().size(); 1595 if (bytes_available > 0) 1596 { 1597 if (bytes_available > buf_size) 1598 { 1599 memcpy(buf, m_profile_data.front().data(), buf_size); 1600 m_profile_data.front().erase(0, buf_size); 1601 bytes_available = buf_size; 1602 } 1603 else 1604 { 1605 memcpy(buf, m_profile_data.front().data(), bytes_available); 1606 m_profile_data.erase(m_profile_data.begin()); 1607 } 1608 } 1609 return bytes_available; 1610} 1611 1612 1613void * 1614MachProcess::ProfileThread(void *arg) 1615{ 1616 MachProcess *proc = (MachProcess*) arg; 1617 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( arg = %p ) thread starting...", __FUNCTION__, arg); 1618 1619#if defined (__APPLE__) 1620 pthread_setname_np ("performance profiling thread"); 1621#endif 1622 1623 while (proc->IsProfilingEnabled()) 1624 { 1625 nub_state_t state = proc->GetState(); 1626 if (state == eStateRunning) 1627 { 1628 std::string data = proc->Task().GetProfileData(proc->GetProfileScanType()); 1629 if (!data.empty()) 1630 { 1631 proc->SignalAsyncProfileData(data.c_str()); 1632 } 1633 } 1634 else if ((state == eStateUnloaded) || (state == eStateDetached) || (state == eStateUnloaded)) 1635 { 1636 // Done. Get out of this thread. 1637 break; 1638 } 1639 1640 // A simple way to set up the profile interval. We can also use select() or dispatch timer source if necessary. 1641 usleep(proc->ProfileInterval()); 1642 } 1643 return NULL; 1644} 1645 1646 1647pid_t 1648MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) 1649{ 1650 // Clear out and clean up from any current state 1651 Clear(); 1652 if (pid != 0) 1653 { 1654 DNBError err; 1655 // Make sure the process exists... 1656 if (::getpgid (pid) < 0) 1657 { 1658 err.SetErrorToErrno(); 1659 const char *err_cstr = err.AsString(); 1660 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "No such process"); 1661 return INVALID_NUB_PROCESS; 1662 } 1663 1664 SetState(eStateAttaching); 1665 m_pid = pid; 1666 // Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set... 1667#if defined (WITH_BKS) 1668 if (IsBKSProcess (pid)) 1669 m_flags |= eMachProcessFlagsUsingBKS; 1670#elif defined (WITH_SPRINGBOARD) 1671 if (IsSBProcess(pid)) 1672 m_flags |= eMachProcessFlagsUsingSBS; 1673#endif 1674 if (!m_task.StartExceptionThread(err)) 1675 { 1676 const char *err_cstr = err.AsString(); 1677 ::snprintf (err_str, err_len, "%s", err_cstr ? err_cstr : "unable to start the exception thread"); 1678 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1679 m_pid = INVALID_NUB_PROCESS; 1680 return INVALID_NUB_PROCESS; 1681 } 1682 1683 errno = 0; 1684 if (::ptrace (PT_ATTACHEXC, pid, 0, 0)) 1685 err.SetError(errno); 1686 else 1687 err.Clear(); 1688 1689 if (err.Success()) 1690 { 1691 m_flags |= eMachProcessFlagsAttached; 1692 // Sleep a bit to let the exception get received and set our process status 1693 // to stopped. 1694 ::usleep(250000); 1695 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid); 1696 return m_pid; 1697 } 1698 else 1699 { 1700 ::snprintf (err_str, err_len, "%s", err.AsString()); 1701 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid); 1702 } 1703 } 1704 return INVALID_NUB_PROCESS; 1705} 1706 1707Genealogy::ThreadActivitySP 1708MachProcess::GetGenealogyInfoForThread (nub_thread_t tid, bool &timed_out) 1709{ 1710 return m_activities.GetGenealogyInfoForThread (m_pid, tid, m_thread_list, m_task.TaskPort(), timed_out); 1711} 1712 1713Genealogy::ProcessExecutableInfoSP 1714MachProcess::GetGenealogyImageInfo (size_t idx) 1715{ 1716 return m_activities.GetProcessExecutableInfosAtIndex (idx); 1717} 1718 1719// Do the process specific setup for attach. If this returns NULL, then there's no 1720// platform specific stuff to be done to wait for the attach. If you get non-null, 1721// pass that token to the CheckForProcess method, and then to CleanupAfterAttach. 1722 1723// Call PrepareForAttach before attaching to a process that has not yet launched 1724// This returns a token that can be passed to CheckForProcess, and to CleanupAfterAttach. 1725// You should call CleanupAfterAttach to free the token, and do whatever other 1726// cleanup seems good. 1727 1728const void * 1729MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err) 1730{ 1731#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 1732 // Tell SpringBoard to halt the next launch of this application on startup. 1733 1734 if (!waitfor) 1735 return NULL; 1736 1737 const char *app_ext = strstr(path, ".app"); 1738 const bool is_app = app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/'); 1739 if (!is_app) 1740 { 1741 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, " 1742 "we can't tell springboard to wait for launch...", 1743 path); 1744 return NULL; 1745 } 1746 1747#if defined (WITH_BKS) 1748 if (launch_flavor == eLaunchFlavorDefault) 1749 launch_flavor = eLaunchFlavorBKS; 1750 if (launch_flavor != eLaunchFlavorBKS) 1751 return NULL; 1752#elif defined (WITH_SPRINGBOARD) 1753 if (launch_flavor == eLaunchFlavorDefault) 1754 launch_flavor = eLaunchFlavorSpringBoard; 1755 if (launch_flavor != eLaunchFlavorSpringBoard) 1756 return NULL; 1757#endif 1758 1759 std::string app_bundle_path(path, app_ext + strlen(".app")); 1760 1761 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path.c_str (), attach_err); 1762 std::string bundleIDStr; 1763 CFString::UTF8(bundleIDCFStr, bundleIDStr); 1764 DNBLogThreadedIf(LOG_PROCESS, 1765 "CopyBundleIDForPath (%s, err_str) returned @\"%s\"", 1766 app_bundle_path.c_str (), 1767 bundleIDStr.c_str()); 1768 1769 if (bundleIDCFStr == NULL) 1770 { 1771 return NULL; 1772 } 1773 1774#if defined (WITH_BKS) 1775 if (launch_flavor == eLaunchFlavorBKS) 1776 { 1777 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1778 1779 NSString *stdio_path = nil; 1780 NSFileManager *file_manager = [NSFileManager defaultManager]; 1781 const char *null_path = "/dev/null"; 1782 stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; 1783 1784 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 1785 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 1786 1787 DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", " 1788 "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )", 1789 bundleIDStr.c_str(), 1790 null_path); 1791 1792 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; 1793 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; 1794 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; 1795 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDebugOnNextLaunch]; 1796 1797 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 1798 1799 BKSSystemService *system_service = [[BKSSystemService alloc] init]; 1800 1801 mach_port_t client_port = [system_service createClientPort]; 1802 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 1803 __block BKSOpenApplicationErrorCode attach_error_code = BKSOpenApplicationErrorCodeNone; 1804 1805 NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; 1806 1807 [system_service openApplication: bundleIDNSStr 1808 options: options 1809 clientPort: client_port 1810 withResult: ^(NSError *error) 1811 { 1812 // The system service will cleanup the client port we created for us. 1813 if (error) 1814 attach_error_code = (BKSOpenApplicationErrorCode)[error code]; 1815 1816 [system_service release]; 1817 dispatch_semaphore_signal(semaphore); 1818 } 1819 ]; 1820 1821 const uint32_t timeout_secs = 9; 1822 1823 dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 1824 1825 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 1826 1827 if (!success) 1828 { 1829 DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); 1830 attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); 1831 attach_err.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 1832 } 1833 else if (attach_error_code != BKSOpenApplicationErrorCodeNone) 1834 { 1835 SetBKSError (attach_error_code, attach_err); 1836 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", 1837 bundleIDStr.c_str(), 1838 attach_error_code); 1839 } 1840 dispatch_release(semaphore); 1841 [pool drain]; 1842 } 1843#elif defined (WITH_SPRINGBOARD) 1844 if (launch_flavor == eLaunchFlavorSpringBoard) 1845 { 1846 SBSApplicationLaunchError sbs_error = 0; 1847 1848 const char *stdout_err = "/dev/null"; 1849 CFString stdio_path; 1850 stdio_path.SetFileSystemRepresentation (stdout_err); 1851 1852 DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" , NULL, NULL, NULL, @\"%s\", @\"%s\", " 1853 "SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger )", 1854 bundleIDStr.c_str(), 1855 stdout_err, 1856 stdout_err); 1857 1858 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1859 (CFURLRef)NULL, // openURL 1860 NULL, // launch_argv.get(), 1861 NULL, // launch_envp.get(), // CFDictionaryRef environment 1862 stdio_path.get(), 1863 stdio_path.get(), 1864 SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger); 1865 1866 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1867 { 1868 attach_err.SetError(sbs_error, DNBError::SpringBoard); 1869 return NULL; 1870 } 1871 } 1872#endif // WITH_SPRINGBOARD 1873 1874 DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); 1875 return bundleIDCFStr; 1876# else // defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 1877 return NULL; 1878#endif 1879} 1880 1881// Pass in the token you got from PrepareForAttach. If there is a process 1882// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS 1883// will be returned. 1884 1885nub_process_t 1886MachProcess::CheckForProcess (const void *attach_token) 1887{ 1888 if (attach_token == NULL) 1889 return INVALID_NUB_PROCESS; 1890 1891#if defined (WITH_BKS) 1892 NSString *bundleIDNSStr = (NSString *) attach_token; 1893 BKSSystemService *systemService = [[BKSSystemService alloc] init]; 1894 pid_t pid = [systemService pidForApplication: bundleIDNSStr]; 1895 [systemService release]; 1896 if (pid == 0) 1897 return INVALID_NUB_PROCESS; 1898 else 1899 return pid; 1900#elif defined (WITH_SPRINGBOARD) 1901 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1902 Boolean got_it; 1903 nub_process_t attach_pid; 1904 got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid); 1905 if (got_it) 1906 return attach_pid; 1907 else 1908 return INVALID_NUB_PROCESS; 1909#else 1910 return INVALID_NUB_PROCESS; 1911#endif 1912} 1913 1914// Call this to clean up after you have either attached or given up on the attach. 1915// Pass true for success if you have attached, false if you have not. 1916// The token will also be freed at this point, so you can't use it after calling 1917// this method. 1918 1919void 1920MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) 1921{ 1922 if (attach_token == NULL) 1923 return; 1924 1925#if defined (WITH_BKS) 1926 1927 if (!success) 1928 { 1929 BKSCleanupAfterAttach (attach_token, err_str); 1930 } 1931 CFRelease((CFStringRef) attach_token); 1932 1933#elif defined (WITH_SPRINGBOARD) 1934 // Tell SpringBoard to cancel the debug on next launch of this application 1935 // if we failed to attach 1936 if (!success) 1937 { 1938 SBSApplicationLaunchError sbs_error = 0; 1939 CFStringRef bundleIDCFStr = (CFStringRef) attach_token; 1940 1941 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 1942 (CFURLRef)NULL, 1943 NULL, 1944 NULL, 1945 NULL, 1946 NULL, 1947 SBSApplicationCancelDebugOnNextLaunch); 1948 1949 if (sbs_error != SBSApplicationLaunchErrorSuccess) 1950 { 1951 err_str.SetError(sbs_error, DNBError::SpringBoard); 1952 return; 1953 } 1954 } 1955 1956 CFRelease((CFStringRef) attach_token); 1957#endif 1958} 1959 1960pid_t 1961MachProcess::LaunchForDebug 1962( 1963 const char *path, 1964 char const *argv[], 1965 char const *envp[], 1966 const char *working_directory, // NULL => don't change, non-NULL => set working directory for inferior to this 1967 const char *stdin_path, 1968 const char *stdout_path, 1969 const char *stderr_path, 1970 bool no_stdio, 1971 nub_launch_flavor_t launch_flavor, 1972 int disable_aslr, 1973 const char *event_data, 1974 DNBError &launch_err 1975) 1976{ 1977 // Clear out and clean up from any current state 1978 Clear(); 1979 1980 DNBLogThreadedIf(LOG_PROCESS, "%s( path = '%s', argv = %p, envp = %p, launch_flavor = %u, disable_aslr = %d )", __FUNCTION__, path, argv, envp, launch_flavor, disable_aslr); 1981 1982 // Fork a child process for debugging 1983 SetState(eStateLaunching); 1984 1985 switch (launch_flavor) 1986 { 1987 case eLaunchFlavorForkExec: 1988 m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); 1989 break; 1990#ifdef WITH_BKS 1991 case eLaunchFlavorBKS: 1992 { 1993 const char *app_ext = strstr(path, ".app"); 1994 if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) 1995 { 1996 std::string app_bundle_path(path, app_ext + strlen(".app")); 1997 if (BKSLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) 1998 return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. 1999 else 2000 break; // We tried a BKS launch, but didn't succeed lets get out 2001 } 2002 } 2003 break; 2004#endif 2005#ifdef WITH_SPRINGBOARD 2006 2007 case eLaunchFlavorSpringBoard: 2008 { 2009 // .../whatever.app/whatever ? 2010 // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in "com.apple.whatever" here 2011 const char *app_ext = strstr (path, ".app/"); 2012 if (app_ext == NULL) 2013 { 2014 // .../whatever.app ? 2015 int len = strlen (path); 2016 if (len > 5) 2017 { 2018 if (strcmp (path + len - 4, ".app") == 0) 2019 { 2020 app_ext = path + len - 4; 2021 } 2022 } 2023 } 2024 if (app_ext) 2025 { 2026 std::string app_bundle_path(path, app_ext + strlen(".app")); 2027 if (SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0) 2028 return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. 2029 else 2030 break; // We tried a springboard launch, but didn't succeed lets get out 2031 } 2032 } 2033 break; 2034 2035#endif 2036 2037 case eLaunchFlavorPosixSpawn: 2038 m_pid = MachProcess::PosixSpawnChildForPTraceDebugging (path, 2039 DNBArchProtocol::GetArchitecture (), 2040 argv, 2041 envp, 2042 working_directory, 2043 stdin_path, 2044 stdout_path, 2045 stderr_path, 2046 no_stdio, 2047 this, 2048 disable_aslr, 2049 launch_err); 2050 break; 2051 2052 default: 2053 // Invalid launch 2054 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2055 return INVALID_NUB_PROCESS; 2056 } 2057 2058 if (m_pid == INVALID_NUB_PROCESS) 2059 { 2060 // If we don't have a valid process ID and no one has set the error, 2061 // then return a generic error 2062 if (launch_err.Success()) 2063 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2064 } 2065 else 2066 { 2067 m_path = path; 2068 size_t i; 2069 char const *arg; 2070 for (i=0; (arg = argv[i]) != NULL; i++) 2071 m_args.push_back(arg); 2072 2073 m_task.StartExceptionThread(launch_err); 2074 if (launch_err.Fail()) 2075 { 2076 if (launch_err.AsString() == NULL) 2077 launch_err.SetErrorString("unable to start the exception thread"); 2078 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2079 ::ptrace (PT_KILL, m_pid, 0, 0); 2080 m_pid = INVALID_NUB_PROCESS; 2081 return INVALID_NUB_PROCESS; 2082 } 2083 2084 StartSTDIOThread(); 2085 2086 if (launch_flavor == eLaunchFlavorPosixSpawn) 2087 { 2088 2089 SetState (eStateAttaching); 2090 errno = 0; 2091 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2092 if (err == 0) 2093 { 2094 m_flags |= eMachProcessFlagsAttached; 2095 DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid); 2096 launch_err.Clear(); 2097 } 2098 else 2099 { 2100 SetState (eStateExited); 2101 DNBError ptrace_err(errno, DNBError::POSIX); 2102 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()); 2103 launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic); 2104 } 2105 } 2106 else 2107 { 2108 launch_err.Clear(); 2109 } 2110 } 2111 return m_pid; 2112} 2113 2114pid_t 2115MachProcess::PosixSpawnChildForPTraceDebugging 2116( 2117 const char *path, 2118 cpu_type_t cpu_type, 2119 char const *argv[], 2120 char const *envp[], 2121 const char *working_directory, 2122 const char *stdin_path, 2123 const char *stdout_path, 2124 const char *stderr_path, 2125 bool no_stdio, 2126 MachProcess* process, 2127 int disable_aslr, 2128 DNBError& err 2129) 2130{ 2131 posix_spawnattr_t attr; 2132 short flags; 2133 DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv=%p, envp=%p, working_dir=%s, stdin=%s, stdout=%s stderr=%s, no-stdio=%i)", 2134 __FUNCTION__, 2135 path, 2136 argv, 2137 envp, 2138 working_directory, 2139 stdin_path, 2140 stdout_path, 2141 stderr_path, 2142 no_stdio); 2143 2144 err.SetError( ::posix_spawnattr_init (&attr), DNBError::POSIX); 2145 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2146 err.LogThreaded("::posix_spawnattr_init ( &attr )"); 2147 if (err.Fail()) 2148 return INVALID_NUB_PROCESS; 2149 2150 flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; 2151 if (disable_aslr) 2152 flags |= _POSIX_SPAWN_DISABLE_ASLR; 2153 2154 sigset_t no_signals; 2155 sigset_t all_signals; 2156 sigemptyset (&no_signals); 2157 sigfillset (&all_signals); 2158 ::posix_spawnattr_setsigmask(&attr, &no_signals); 2159 ::posix_spawnattr_setsigdefault(&attr, &all_signals); 2160 2161 err.SetError( ::posix_spawnattr_setflags (&attr, flags), DNBError::POSIX); 2162 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2163 err.LogThreaded("::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )", flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" : ""); 2164 if (err.Fail()) 2165 return INVALID_NUB_PROCESS; 2166 2167 // Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail 2168 // and we will fail to continue with our process... 2169 2170 // On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment.... 2171 2172#if !defined(__arm__) 2173 2174 // We don't need to do this for ARM, and we really shouldn't now that we 2175 // have multiple CPU subtypes and no posix_spawnattr call that allows us 2176 // to set which CPU subtype to launch... 2177 if (cpu_type != 0) 2178 { 2179 size_t ocount = 0; 2180 err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount), DNBError::POSIX); 2181 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2182 err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu_type, (uint64_t)ocount); 2183 2184 if (err.Fail() != 0 || ocount != 1) 2185 return INVALID_NUB_PROCESS; 2186 } 2187#endif 2188 2189 PseudoTerminal pty; 2190 2191 posix_spawn_file_actions_t file_actions; 2192 err.SetError( ::posix_spawn_file_actions_init (&file_actions), DNBError::POSIX); 2193 int file_actions_valid = err.Success(); 2194 if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS)) 2195 err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )"); 2196 int pty_error = -1; 2197 pid_t pid = INVALID_NUB_PROCESS; 2198 if (file_actions_valid) 2199 { 2200 if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL && !no_stdio) 2201 { 2202 pty_error = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2203 if (pty_error == PseudoTerminal::success) 2204 { 2205 stdin_path = stdout_path = stderr_path = pty.SlaveName(); 2206 } 2207 } 2208 2209 // if no_stdio or std paths not supplied, then route to "/dev/null". 2210 if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0') 2211 stdin_path = "/dev/null"; 2212 if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0') 2213 stdout_path = "/dev/null"; 2214 if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0') 2215 stderr_path = "/dev/null"; 2216 2217 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2218 STDIN_FILENO, 2219 stdin_path, 2220 O_RDONLY | O_NOCTTY, 2221 0), 2222 DNBError::POSIX); 2223 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2224 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDIN_FILENO, path='%s')", stdin_path); 2225 2226 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2227 STDOUT_FILENO, 2228 stdout_path, 2229 O_WRONLY | O_NOCTTY | O_CREAT, 2230 0640), 2231 DNBError::POSIX); 2232 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2233 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDOUT_FILENO, path='%s')", stdout_path); 2234 2235 err.SetError( ::posix_spawn_file_actions_addopen (&file_actions, 2236 STDERR_FILENO, 2237 stderr_path, 2238 O_WRONLY | O_NOCTTY | O_CREAT, 2239 0640), 2240 DNBError::POSIX); 2241 if (err.Fail() || DNBLogCheckLogBit (LOG_PROCESS)) 2242 err.LogThreaded ("::posix_spawn_file_actions_addopen (&file_actions, filedes=STDERR_FILENO, path='%s')", stderr_path); 2243 2244 // TODO: Verify if we can set the working directory back immediately 2245 // after the posix_spawnp call without creating a race condition??? 2246 if (working_directory) 2247 ::chdir (working_directory); 2248 2249 err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 2250 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2251 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); 2252 } 2253 else 2254 { 2255 // TODO: Verify if we can set the working directory back immediately 2256 // after the posix_spawnp call without creating a race condition??? 2257 if (working_directory) 2258 ::chdir (working_directory); 2259 2260 err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), DNBError::POSIX); 2261 if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2262 err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); 2263 } 2264 2265 // We have seen some cases where posix_spawnp was returning a valid 2266 // looking pid even when an error was returned, so clear it out 2267 if (err.Fail()) 2268 pid = INVALID_NUB_PROCESS; 2269 2270 if (pty_error == 0) 2271 { 2272 if (process != NULL) 2273 { 2274 int master_fd = pty.ReleaseMasterFD(); 2275 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2276 } 2277 } 2278 ::posix_spawnattr_destroy (&attr); 2279 2280 if (pid != INVALID_NUB_PROCESS) 2281 { 2282 cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess (pid); 2283 DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x", __FUNCTION__, pid, pid_cpu_type); 2284 if (pid_cpu_type) 2285 DNBArchProtocol::SetArchitecture (pid_cpu_type); 2286 } 2287 2288 if (file_actions_valid) 2289 { 2290 DNBError err2; 2291 err2.SetError( ::posix_spawn_file_actions_destroy (&file_actions), DNBError::POSIX); 2292 if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS)) 2293 err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )"); 2294 } 2295 2296 return pid; 2297} 2298 2299uint32_t 2300MachProcess::GetCPUTypeForLocalProcess (pid_t pid) 2301{ 2302 int mib[CTL_MAXNAME]={0,}; 2303 size_t len = CTL_MAXNAME; 2304 if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) 2305 return 0; 2306 2307 mib[len] = pid; 2308 len++; 2309 2310 cpu_type_t cpu; 2311 size_t cpu_len = sizeof(cpu); 2312 if (::sysctl (mib, len, &cpu, &cpu_len, 0, 0)) 2313 cpu = 0; 2314 return cpu; 2315} 2316 2317pid_t 2318MachProcess::ForkChildForPTraceDebugging 2319( 2320 const char *path, 2321 char const *argv[], 2322 char const *envp[], 2323 MachProcess* process, 2324 DNBError& launch_err 2325) 2326{ 2327 PseudoTerminal::Error pty_error = PseudoTerminal::success; 2328 2329 // Use a fork that ties the child process's stdin/out/err to a pseudo 2330 // terminal so we can read it in our MachProcess::STDIOThread 2331 // as unbuffered io. 2332 PseudoTerminal pty; 2333 pid_t pid = pty.Fork(pty_error); 2334 2335 if (pid < 0) 2336 { 2337 //-------------------------------------------------------------- 2338 // Error during fork. 2339 //-------------------------------------------------------------- 2340 return pid; 2341 } 2342 else if (pid == 0) 2343 { 2344 //-------------------------------------------------------------- 2345 // Child process 2346 //-------------------------------------------------------------- 2347 ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process 2348 ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions 2349 2350 // If our parent is setgid, lets make sure we don't inherit those 2351 // extra powers due to nepotism. 2352 if (::setgid (getgid ()) == 0) 2353 { 2354 2355 // Let the child have its own process group. We need to execute 2356 // this call in both the child and parent to avoid a race condition 2357 // between the two processes. 2358 ::setpgid (0, 0); // Set the child process group to match its pid 2359 2360 // Sleep a bit to before the exec call 2361 ::sleep (1); 2362 2363 // Turn this process into 2364 ::execv (path, (char * const *)argv); 2365 } 2366 // Exit with error code. Child process should have taken 2367 // over in above exec call and if the exec fails it will 2368 // exit the child process below. 2369 ::exit (127); 2370 } 2371 else 2372 { 2373 //-------------------------------------------------------------- 2374 // Parent process 2375 //-------------------------------------------------------------- 2376 // Let the child have its own process group. We need to execute 2377 // this call in both the child and parent to avoid a race condition 2378 // between the two processes. 2379 ::setpgid (pid, pid); // Set the child process group to match its pid 2380 2381 if (process != NULL) 2382 { 2383 // Release our master pty file descriptor so the pty class doesn't 2384 // close it and so we can continue to use it in our STDIO thread 2385 int master_fd = pty.ReleaseMasterFD(); 2386 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2387 } 2388 } 2389 return pid; 2390} 2391 2392#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 2393// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, 2394// or NULL if there was some problem getting the bundle id. 2395static CFStringRef 2396CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) 2397{ 2398 CFBundle bundle(app_bundle_path); 2399 CFStringRef bundleIDCFStr = bundle.GetIdentifier(); 2400 std::string bundleID; 2401 if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) 2402 { 2403 struct stat app_bundle_stat; 2404 char err_msg[PATH_MAX]; 2405 2406 if (::stat (app_bundle_path, &app_bundle_stat) < 0) 2407 { 2408 err_str.SetError(errno, DNBError::POSIX); 2409 snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); 2410 err_str.SetErrorString(err_msg); 2411 DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); 2412 } 2413 else 2414 { 2415 err_str.SetError(-1, DNBError::Generic); 2416 snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); 2417 err_str.SetErrorString(err_msg); 2418 DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); 2419 } 2420 return NULL; 2421 } 2422 2423 DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); 2424 CFRetain (bundleIDCFStr); 2425 2426 return bundleIDCFStr; 2427} 2428#endif // #if defined 9WITH_SPRINGBOARD) || defined (WITH_BKS) 2429#ifdef WITH_SPRINGBOARD 2430 2431pid_t 2432MachProcess::SBLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, DNBError &launch_err) 2433{ 2434 // Clear out and clean up from any current state 2435 Clear(); 2436 2437 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 2438 2439 // Fork a child process for debugging 2440 SetState(eStateLaunching); 2441 m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio, this, launch_err); 2442 if (m_pid != 0) 2443 { 2444 m_flags |= eMachProcessFlagsUsingSBS; 2445 m_path = path; 2446 size_t i; 2447 char const *arg; 2448 for (i=0; (arg = argv[i]) != NULL; i++) 2449 m_args.push_back(arg); 2450 m_task.StartExceptionThread(launch_err); 2451 2452 if (launch_err.Fail()) 2453 { 2454 if (launch_err.AsString() == NULL) 2455 launch_err.SetErrorString("unable to start the exception thread"); 2456 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2457 ::ptrace (PT_KILL, m_pid, 0, 0); 2458 m_pid = INVALID_NUB_PROCESS; 2459 return INVALID_NUB_PROCESS; 2460 } 2461 2462 StartSTDIOThread(); 2463 SetState (eStateAttaching); 2464 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2465 if (err == 0) 2466 { 2467 m_flags |= eMachProcessFlagsAttached; 2468 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 2469 } 2470 else 2471 { 2472 SetState (eStateExited); 2473 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 2474 } 2475 } 2476 return m_pid; 2477} 2478 2479#include <servers/bootstrap.h> 2480 2481pid_t 2482MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, MachProcess* process, DNBError &launch_err) 2483{ 2484 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); 2485 CFAllocatorRef alloc = kCFAllocatorDefault; 2486 2487 if (argv[0] == NULL) 2488 return INVALID_NUB_PROCESS; 2489 2490 size_t argc = 0; 2491 // Count the number of arguments 2492 while (argv[argc] != NULL) 2493 argc++; 2494 2495 // Enumerate the arguments 2496 size_t first_launch_arg_idx = 1; 2497 CFReleaser<CFMutableArrayRef> launch_argv; 2498 2499 if (argv[first_launch_arg_idx]) 2500 { 2501 size_t launch_argc = argc > 0 ? argc - 1 : 0; 2502 launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); 2503 size_t i; 2504 char const *arg; 2505 CFString launch_arg; 2506 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 2507 { 2508 launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); 2509 if (launch_arg.get() != NULL) 2510 CFArrayAppendValue(launch_argv.get(), launch_arg.get()); 2511 else 2512 break; 2513 } 2514 } 2515 2516 // Next fill in the arguments dictionary. Note, the envp array is of the form 2517 // Variable=value but SpringBoard wants a CF dictionary. So we have to convert 2518 // this here. 2519 2520 CFReleaser<CFMutableDictionaryRef> launch_envp; 2521 2522 if (envp[0]) 2523 { 2524 launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 2525 const char *value; 2526 int name_len; 2527 CFString name_string, value_string; 2528 2529 for (int i = 0; envp[i] != NULL; i++) 2530 { 2531 value = strstr (envp[i], "="); 2532 2533 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 2534 if (value == NULL || value == envp[i]) 2535 continue; 2536 2537 name_len = value - envp[i]; 2538 2539 // Now move value over the "=" 2540 value++; 2541 2542 name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); 2543 value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); 2544 CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); 2545 } 2546 } 2547 2548 CFString stdio_path; 2549 2550 PseudoTerminal pty; 2551 if (!no_stdio) 2552 { 2553 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2554 if (pty_err == PseudoTerminal::success) 2555 { 2556 const char* slave_name = pty.SlaveName(); 2557 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 2558 if (slave_name && slave_name[0]) 2559 { 2560 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2561 stdio_path.SetFileSystemRepresentation (slave_name); 2562 } 2563 } 2564 } 2565 2566 if (stdio_path.get() == NULL) 2567 { 2568 stdio_path.SetFileSystemRepresentation ("/dev/null"); 2569 } 2570 2571 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2572 if (bundleIDCFStr == NULL) 2573 return INVALID_NUB_PROCESS; 2574 2575 // This is just for logging: 2576 std::string bundleID; 2577 CFString::UTF8(bundleIDCFStr, bundleID); 2578 2579 DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); 2580 2581 // Find SpringBoard 2582 SBSApplicationLaunchError sbs_error = 0; 2583 sbs_error = SBSLaunchApplicationForDebugging (bundleIDCFStr, 2584 (CFURLRef)NULL, // openURL 2585 launch_argv.get(), 2586 launch_envp.get(), // CFDictionaryRef environment 2587 stdio_path.get(), 2588 stdio_path.get(), 2589 SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); 2590 2591 2592 launch_err.SetError(sbs_error, DNBError::SpringBoard); 2593 2594 if (sbs_error == SBSApplicationLaunchErrorSuccess) 2595 { 2596 static const useconds_t pid_poll_interval = 200000; 2597 static const useconds_t pid_poll_timeout = 30000000; 2598 2599 useconds_t pid_poll_total = 0; 2600 2601 nub_process_t pid = INVALID_NUB_PROCESS; 2602 Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2603 // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired 2604 // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started 2605 // yet, or that it died very quickly (if you weren't using waitForDebugger). 2606 while (!pid_found && pid_poll_total < pid_poll_timeout) 2607 { 2608 usleep (pid_poll_interval); 2609 pid_poll_total += pid_poll_interval; 2610 DNBLogThreadedIf(LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); 2611 pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); 2612 } 2613 2614 CFRelease (bundleIDCFStr); 2615 if (pid_found) 2616 { 2617 if (process != NULL) 2618 { 2619 // Release our master pty file descriptor so the pty class doesn't 2620 // close it and so we can continue to use it in our STDIO thread 2621 int master_fd = pty.ReleaseMasterFD(); 2622 process->SetChildFileDescriptors(master_fd, master_fd, master_fd); 2623 } 2624 DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); 2625 } 2626 else 2627 { 2628 DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); 2629 } 2630 return pid; 2631 } 2632 2633 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); 2634 return INVALID_NUB_PROCESS; 2635} 2636 2637#endif // #ifdef WITH_SPRINGBOARD 2638 2639#ifdef WITH_BKS 2640 2641 2642// This function runs the BKSSystemService method openApplication:options:clientPort:withResult, 2643// messaging the app passed in bundleIDNSStr. 2644// The function should be run inside of an NSAutoReleasePool. 2645// 2646// It will use the "options" dictionary passed in, and fill the error passed in if there is an error. 2647// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID. 2648// If bundleIDNSStr is NULL, then the system application will be messaged. 2649 2650static bool 2651CallBKSSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid) 2652{ 2653 // Now make our systemService: 2654 BKSSystemService *system_service = [[BKSSystemService alloc] init]; 2655 2656 if (bundleIDNSStr == nil) 2657 { 2658 bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; 2659 if (bundleIDNSStr == nil) 2660 { 2661 // Okay, no system app... 2662 error.SetErrorString("No system application to message."); 2663 return false; 2664 } 2665 } 2666 2667 mach_port_t client_port = [system_service createClientPort]; 2668 __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 2669 __block BKSOpenApplicationErrorCode open_app_error = BKSOpenApplicationErrorCodeNone; 2670 bool wants_pid = (return_pid != NULL); 2671 __block pid_t pid_in_block; 2672 2673 const char *cstr = [bundleIDNSStr UTF8String]; 2674 if (!cstr) 2675 cstr = "<Unknown Bundle ID>"; 2676 2677 DNBLog ("About to launch process for bundle ID: %s", cstr); 2678 [system_service openApplication: bundleIDNSStr 2679 options: options 2680 clientPort: client_port 2681 withResult: ^(NSError *bks_error) 2682 { 2683 // The system service will cleanup the client port we created for us. 2684 if (bks_error) 2685 open_app_error = (BKSOpenApplicationErrorCode)[bks_error code]; 2686 2687 if (open_app_error == BKSOpenApplicationErrorCodeNone) 2688 { 2689 if (wants_pid) 2690 { 2691 pid_in_block = [system_service pidForApplication: bundleIDNSStr]; 2692 DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block); 2693 DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block); 2694 } 2695 else 2696 DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success."); 2697 } 2698 else 2699 { 2700 const char *error_str = [[bks_error localizedDescription] UTF8String]; 2701 DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%d).", 2702 error_str ? error_str : "<unknown error>", 2703 open_app_error); 2704 // REMOVE ME 2705 DNBLogError ("In completion handler for send event, got error \"%s\"(%d).", 2706 error_str ? error_str : "<unknown error>", 2707 open_app_error); 2708 } 2709 2710 [system_service release]; 2711 dispatch_semaphore_signal(semaphore); 2712 } 2713 2714 ]; 2715 2716 const uint32_t timeout_secs = 9; 2717 2718 dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); 2719 2720 long success = dispatch_semaphore_wait(semaphore, timeout) == 0; 2721 2722 dispatch_release(semaphore); 2723 2724 if (!success) 2725 { 2726 DNBLogError("timed out trying to send openApplication to %s.", cstr); 2727 error.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); 2728 error.SetErrorString ("timed out trying to launch app"); 2729 } 2730 else if (open_app_error != BKSOpenApplicationErrorCodeNone) 2731 { 2732 SetBKSError (open_app_error, error); 2733 DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error); 2734 success = false; 2735 } 2736 else if (wants_pid) 2737 { 2738 *return_pid = pid_in_block; 2739 DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid); 2740 } 2741 2742 2743 return success; 2744} 2745 2746void 2747MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str) 2748{ 2749 bool success; 2750 2751 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2752 2753 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: 2754 NSString *bundleIDNSStr = (NSString *) attach_token; 2755 2756 // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: 2757 2758 // First we have the debug sub-dictionary: 2759 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 2760 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch]; 2761 2762 // That will go in the overall dictionary: 2763 2764 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 2765 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 2766 2767 success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, err_str, NULL); 2768 2769 if (!success) 2770 { 2771 DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); 2772 } 2773 2774 [pool drain]; 2775} 2776 2777bool 2778AddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) 2779{ 2780 if (strcmp (event_data, "BackgroundContentFetching") == 0) 2781 { 2782 DNBLog("Setting ActivateForEvent key in options dictionary."); 2783 NSDictionary *event_details = [NSDictionary dictionary]; 2784 NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching]; 2785 [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent]; 2786 return true; 2787 } 2788 else 2789 { 2790 DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); 2791 option_error.SetErrorString("Unrecognized event data."); 2792 return false; 2793 } 2794 2795} 2796 2797pid_t 2798MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err) 2799{ 2800 // Clear out and clean up from any current state 2801 Clear(); 2802 2803 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); 2804 2805 // Fork a child process for debugging 2806 SetState(eStateLaunching); 2807 m_pid = BKSForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); 2808 if (m_pid != 0) 2809 { 2810 m_flags |= eMachProcessFlagsUsingBKS; 2811 m_path = path; 2812 size_t i; 2813 char const *arg; 2814 for (i=0; (arg = argv[i]) != NULL; i++) 2815 m_args.push_back(arg); 2816 m_task.StartExceptionThread(launch_err); 2817 2818 if (launch_err.Fail()) 2819 { 2820 if (launch_err.AsString() == NULL) 2821 launch_err.SetErrorString("unable to start the exception thread"); 2822 DNBLog ("Could not get inferior's Mach exception port, sending ptrace PT_KILL and exiting."); 2823 ::ptrace (PT_KILL, m_pid, 0, 0); 2824 m_pid = INVALID_NUB_PROCESS; 2825 return INVALID_NUB_PROCESS; 2826 } 2827 2828 StartSTDIOThread(); 2829 SetState (eStateAttaching); 2830 int err = ::ptrace (PT_ATTACHEXC, m_pid, 0, 0); 2831 if (err == 0) 2832 { 2833 m_flags |= eMachProcessFlagsAttached; 2834 DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid); 2835 } 2836 else 2837 { 2838 SetState (eStateExited); 2839 DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid); 2840 } 2841 } 2842 return m_pid; 2843} 2844 2845pid_t 2846MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path, 2847 char const *argv[], 2848 char const *envp[], 2849 bool no_stdio, 2850 bool disable_aslr, 2851 const char *event_data, 2852 DNBError &launch_err) 2853{ 2854 if (argv[0] == NULL) 2855 return INVALID_NUB_PROCESS; 2856 2857 DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, this); 2858 2859 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 2860 2861 size_t argc = 0; 2862 // Count the number of arguments 2863 while (argv[argc] != NULL) 2864 argc++; 2865 2866 // Enumerate the arguments 2867 size_t first_launch_arg_idx = 1; 2868 2869 NSMutableArray *launch_argv = nil; 2870 2871 if (argv[first_launch_arg_idx]) 2872 { 2873 size_t launch_argc = argc > 0 ? argc - 1 : 0; 2874 launch_argv = [NSMutableArray arrayWithCapacity: launch_argc]; 2875 size_t i; 2876 char const *arg; 2877 NSString *launch_arg; 2878 for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) 2879 { 2880 launch_arg = [NSString stringWithUTF8String: arg]; 2881 // FIXME: Should we silently eat an argument that we can't convert into a UTF8 string? 2882 if (launch_arg != nil) 2883 [launch_argv addObject: launch_arg]; 2884 else 2885 break; 2886 } 2887 } 2888 2889 NSMutableDictionary *launch_envp = nil; 2890 if (envp[0]) 2891 { 2892 launch_envp = [[NSMutableDictionary alloc] init]; 2893 const char *value; 2894 int name_len; 2895 NSString *name_string, *value_string; 2896 2897 for (int i = 0; envp[i] != NULL; i++) 2898 { 2899 value = strstr (envp[i], "="); 2900 2901 // If the name field is empty or there's no =, skip it. Somebody's messing with us. 2902 if (value == NULL || value == envp[i]) 2903 continue; 2904 2905 name_len = value - envp[i]; 2906 2907 // Now move value over the "=" 2908 value++; 2909 name_string = [[NSString alloc] initWithBytes: envp[i] length: name_len encoding: NSUTF8StringEncoding]; 2910 value_string = [NSString stringWithUTF8String: value]; 2911 [launch_envp setObject: value_string forKey: name_string]; 2912 } 2913 } 2914 2915 NSString *stdio_path = nil; 2916 NSFileManager *file_manager = [NSFileManager defaultManager]; 2917 2918 PseudoTerminal pty; 2919 if (!no_stdio) 2920 { 2921 PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); 2922 if (pty_err == PseudoTerminal::success) 2923 { 2924 const char* slave_name = pty.SlaveName(); 2925 DNBLogThreadedIf(LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); 2926 if (slave_name && slave_name[0]) 2927 { 2928 ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); 2929 stdio_path = [file_manager stringWithFileSystemRepresentation: slave_name length: strlen(slave_name)]; 2930 } 2931 } 2932 } 2933 2934 if (stdio_path == nil) 2935 { 2936 const char *null_path = "/dev/null"; 2937 stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; 2938 } 2939 2940 CFStringRef bundleIDCFStr = CopyBundleIDForPath (app_bundle_path, launch_err); 2941 if (bundleIDCFStr == NULL) 2942 { 2943 [pool drain]; 2944 return INVALID_NUB_PROCESS; 2945 } 2946 2947 // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: 2948 NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; 2949 2950 // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: 2951 2952 // First we have the debug sub-dictionary: 2953 NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; 2954 if (launch_argv != nil) 2955 [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments]; 2956 if (launch_envp != nil) 2957 [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment]; 2958 2959 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; 2960 [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; 2961 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; 2962 if (disable_aslr) 2963 [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR]; 2964 2965 // That will go in the overall dictionary: 2966 2967 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 2968 [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; 2969 2970 // For now we only support one kind of event: the "fetch" event, which is indicated by the fact that its data 2971 // is an empty dictionary. 2972 if (event_data != NULL && *event_data != '\0') 2973 { 2974 if (!AddEventDataToOptions(options, event_data, launch_err)) 2975 { 2976 [pool drain]; 2977 return INVALID_NUB_PROCESS; 2978 } 2979 } 2980 2981 // And there are some other options at the top level in this dictionary: 2982 [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice]; 2983 2984 pid_t return_pid = INVALID_NUB_PROCESS; 2985 bool success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, launch_err, &return_pid); 2986 2987 if (success) 2988 { 2989 int master_fd = pty.ReleaseMasterFD(); 2990 SetChildFileDescriptors(master_fd, master_fd, master_fd); 2991 CFString::UTF8(bundleIDCFStr, m_bundle_id); 2992 } 2993 2994 [pool drain]; 2995 2996 return return_pid; 2997} 2998 2999bool 3000MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) 3001{ 3002 bool return_value = true; 3003 3004 if (event_data == NULL || *event_data == '\0') 3005 { 3006 DNBLogError ("SendEvent called with NULL event data."); 3007 send_err.SetErrorString("SendEvent called with empty event data"); 3008 return false; 3009 } 3010 3011 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 3012 3013 if (strcmp (event_data, "BackgroundApplication") == 0) 3014 { 3015 // This is an event I cooked up. What you actually do is foreground the system app, so: 3016 return_value = CallBKSSystemServiceOpenApplication(nil, nil, send_err, NULL); 3017 if (!return_value) 3018 { 3019 DNBLogError ("Failed to background application, error: %s.", send_err.AsString()); 3020 } 3021 } 3022 else 3023 { 3024 if (m_bundle_id.empty()) 3025 { 3026 // See if we can figure out the bundle ID for this PID: 3027 3028 DNBLogError ("Tried to send event \"%s\" to a process that has no bundle ID.", event_data); 3029 return false; 3030 } 3031 3032 NSString *bundleIDNSStr = [NSString stringWithUTF8String:m_bundle_id.c_str()]; 3033 3034 NSMutableDictionary *options = [NSMutableDictionary dictionary]; 3035 3036 if (!AddEventDataToOptions(options, event_data, send_err)) 3037 { 3038 [pool drain]; 3039 return false; 3040 } 3041 3042 3043 return_value = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, send_err, NULL); 3044 3045 if (!return_value) 3046 { 3047 DNBLogError ("Failed to send event: %s, error: %s.", event_data, send_err.AsString()); 3048 } 3049 } 3050 3051 [pool drain]; 3052 return return_value; 3053} 3054#endif // WITH_BKS 3055