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