| //===-- Host.mm -------------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <dlfcn.h> |
| #include <libgen.h> |
| #include <mach/mach.h> |
| #include <mach-o/dyld.h> |
| #include <signal.h> |
| #include <stddef.h> |
| #include <sys/sysctl.h> |
| #include <unistd.h> |
| |
| #include <map> |
| #include <string> |
| |
| #include <objc/objc-auto.h> |
| |
| #include <Foundation/Foundation.h> |
| |
| #include "CFCBundle.h" |
| #include "CFCReleaser.h" |
| #include "CFCString.h" |
| |
| #include "lldb/Host/Host.h" |
| #include "lldb/Core/ArchSpec.h" |
| #include "lldb/Core/ConstString.h" |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/FileSpec.h" |
| #include "lldb/Core/Log.h" |
| #include "lldb/Core/StreamString.h" |
| #include "lldb/Host/Mutex.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/TargetList.h" |
| #include "lldb/lldb-private-log.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| //------------------------------------------------------------------ |
| // Return the size in bytes of a page on the host system |
| //------------------------------------------------------------------ |
| size_t |
| Host::GetPageSize() |
| { |
| return ::getpagesize(); |
| } |
| |
| |
| //------------------------------------------------------------------ |
| // Returns true if the host system is Big Endian. |
| //------------------------------------------------------------------ |
| ByteOrder |
| Host::GetByteOrder() |
| { |
| union EndianTest |
| { |
| uint32_t num; |
| uint8_t bytes[sizeof(uint32_t)]; |
| } endian = { (uint16_t)0x11223344 }; |
| switch (endian.bytes[0]) |
| { |
| case 0x11: return eByteOrderLittle; |
| case 0x44: return eByteOrderBig; |
| case 0x33: return eByteOrderPDP; |
| } |
| return eByteOrderInvalid; |
| } |
| |
| lldb::pid_t |
| Host::GetCurrentProcessID() |
| { |
| return ::getpid(); |
| } |
| |
| lldb::pid_t |
| Host::GetCurrentThreadID() |
| { |
| return ::mach_thread_self(); |
| } |
| |
| |
| const ArchSpec & |
| Host::GetArchitecture () |
| { |
| static ArchSpec g_host_arch; |
| if (!g_host_arch.IsValid()) |
| { |
| uint32_t cputype, cpusubtype; |
| uint32_t is_64_bit_capable; |
| size_t len = sizeof(cputype); |
| if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) |
| { |
| len = sizeof(cpusubtype); |
| if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) |
| g_host_arch.SetArch(cputype, cpusubtype); |
| |
| len = sizeof (is_64_bit_capable); |
| if (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0) |
| { |
| if (is_64_bit_capable) |
| { |
| if (cputype == CPU_TYPE_I386 && cpusubtype == CPU_SUBTYPE_486) |
| cpusubtype = CPU_SUBTYPE_I386_ALL; |
| |
| cputype |= CPU_ARCH_ABI64; |
| } |
| } |
| } |
| } |
| return g_host_arch; |
| } |
| |
| const ConstString & |
| Host::GetVendorString() |
| { |
| static ConstString g_vendor; |
| if (!g_vendor) |
| { |
| char ostype[64]; |
| size_t len = sizeof(ostype); |
| if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0) |
| g_vendor.SetCString (ostype); |
| } |
| return g_vendor; |
| } |
| |
| const ConstString & |
| Host::GetOSString() |
| { |
| static ConstString g_os_string("apple"); |
| return g_os_string; |
| } |
| |
| const ConstString & |
| Host::GetTargetTriple() |
| { |
| static ConstString g_host_triple; |
| if (!(g_host_triple)) |
| { |
| StreamString triple; |
| triple.Printf("%s-%s-%s", |
| GetArchitecture ().AsCString(), |
| GetVendorString().AsCString("apple"), |
| GetOSString().AsCString("darwin")); |
| |
| std::transform (triple.GetString().begin(), |
| triple.GetString().end(), |
| triple.GetString().begin(), |
| ::tolower); |
| |
| g_host_triple.SetCString(triple.GetString().c_str()); |
| } |
| return g_host_triple; |
| } |
| |
| class MacOSXDarwinThread |
| { |
| public: |
| MacOSXDarwinThread(const char *thread_name) : |
| m_pool (nil) |
| { |
| // Register our thread with the collector if garbage collection is enabled. |
| if (objc_collectingEnabled()) |
| { |
| #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 |
| // On Leopard and earlier there is no way objc_registerThreadWithCollector |
| // function, so we do it manually. |
| auto_zone_register_thread(auto_zone()); |
| #else |
| // On SnowLoepard and later we just call the thread registration function. |
| objc_registerThreadWithCollector(); |
| #endif |
| } |
| else |
| { |
| m_pool = [[NSAutoreleasePool alloc] init]; |
| } |
| |
| |
| Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name); |
| } |
| |
| ~MacOSXDarwinThread() |
| { |
| if (m_pool) |
| [m_pool release]; |
| } |
| |
| static void PThreadDestructor (void *v) |
| { |
| delete (MacOSXDarwinThread*)v; |
| } |
| |
| protected: |
| NSAutoreleasePool * m_pool; |
| private: |
| DISALLOW_COPY_AND_ASSIGN (MacOSXDarwinThread); |
| }; |
| |
| static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; |
| static pthread_key_t g_thread_create_key = 0; |
| |
| static void |
| InitThreadCreated() |
| { |
| ::pthread_key_create (&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor); |
| } |
| |
| typedef struct HostThreadCreateInfo |
| { |
| std::string thread_name; |
| thread_func_t thread_fptr; |
| thread_arg_t thread_arg; |
| |
| HostThreadCreateInfo (const char *name, thread_func_t fptr, thread_arg_t arg) : |
| thread_name (name ? name : ""), |
| thread_fptr (fptr), |
| thread_arg (arg) |
| { |
| } |
| }; |
| |
| static thread_result_t |
| ThreadCreateTrampoline (thread_arg_t arg) |
| { |
| HostThreadCreateInfo *info = (HostThreadCreateInfo *)arg; |
| Host::ThreadCreated (info->thread_name.c_str()); |
| thread_func_t thread_fptr = info->thread_fptr; |
| thread_arg_t thread_arg = info->thread_arg; |
| |
| Log * log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD); |
| if (log) |
| log->Printf("thread created"); |
| |
| delete info; |
| return thread_fptr (thread_arg); |
| } |
| |
| lldb::thread_t |
| Host::ThreadCreate |
| ( |
| const char *thread_name, |
| thread_func_t thread_fptr, |
| thread_arg_t thread_arg, |
| Error *error |
| ) |
| { |
| lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; |
| |
| // Host::ThreadCreateTrampoline will delete this pointer for us. |
| HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo (thread_name, thread_fptr, thread_arg); |
| |
| int err = ::pthread_create (&thread, NULL, ThreadCreateTrampoline, info_ptr); |
| if (err == 0) |
| { |
| if (error) |
| error->Clear(); |
| return thread; |
| } |
| |
| if (error) |
| error->SetError (err, eErrorTypePOSIX); |
| |
| return LLDB_INVALID_HOST_THREAD; |
| } |
| |
| bool |
| Host::ThreadCancel (lldb::thread_t thread, Error *error) |
| { |
| |
| int err = ::pthread_cancel (thread); |
| if (error) |
| error->SetError(err, eErrorTypePOSIX); |
| return err == 0; |
| } |
| |
| bool |
| Host::ThreadDetach (lldb::thread_t thread, Error *error) |
| { |
| int err = ::pthread_detach (thread); |
| if (error) |
| error->SetError(err, eErrorTypePOSIX); |
| return err == 0; |
| } |
| |
| bool |
| Host::ThreadJoin (lldb::thread_t thread, thread_result_t *thread_result_ptr, Error *error) |
| { |
| int err = ::pthread_join (thread, thread_result_ptr); |
| if (error) |
| error->SetError(err, eErrorTypePOSIX); |
| return err == 0; |
| } |
| |
| void |
| Host::ThreadCreated (const char *thread_name) |
| { |
| ::pthread_once (&g_thread_create_once, InitThreadCreated); |
| if (g_thread_create_key) |
| { |
| ::pthread_setspecific (g_thread_create_key, new MacOSXDarwinThread(thread_name)); |
| } |
| } |
| |
| //------------------------------------------------------------------ |
| // Control access to a static file thread name map using a single |
| // static function to avoid a static constructor. |
| //------------------------------------------------------------------ |
| static const char * |
| ThreadNameAccessor (bool get, lldb::pid_t pid, lldb::tid_t tid, const char *name) |
| { |
| |
| uint64_t pid_tid = ((uint64_t)pid << 32) | (uint64_t)tid; |
| |
| static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; |
| Mutex::Locker locker(&g_mutex); |
| |
| typedef std::map<uint64_t, std::string> thread_name_map; |
| static thread_name_map g_thread_names; |
| |
| if (get) |
| { |
| // See if the thread name exists in our thread name pool |
| thread_name_map::iterator pos = g_thread_names.find(pid_tid); |
| if (pos != g_thread_names.end()) |
| return pos->second.c_str(); |
| } |
| else |
| { |
| // Set the thread name |
| g_thread_names[pid_tid] = name; |
| } |
| return NULL; |
| } |
| |
| |
| |
| const char * |
| Host::GetSignalAsCString (int signo) |
| { |
| switch (signo) |
| { |
| case SIGHUP: return "SIGHUP"; // 1 hangup |
| case SIGINT: return "SIGINT"; // 2 interrupt |
| case SIGQUIT: return "SIGQUIT"; // 3 quit |
| case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught) |
| case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught) |
| case SIGABRT: return "SIGABRT"; // 6 abort() |
| #if defined(_POSIX_C_SOURCE) |
| case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) |
| #else // !_POSIX_C_SOURCE |
| case SIGEMT: return "SIGEMT"; // 7 EMT instruction |
| #endif // !_POSIX_C_SOURCE |
| case SIGFPE: return "SIGFPE"; // 8 floating point exception |
| case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored) |
| case SIGBUS: return "SIGBUS"; // 10 bus error |
| case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation |
| case SIGSYS: return "SIGSYS"; // 12 bad argument to system call |
| case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it |
| case SIGALRM: return "SIGALRM"; // 14 alarm clock |
| case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill |
| case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel |
| case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty |
| case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty |
| case SIGCONT: return "SIGCONT"; // 19 continue a stopped process |
| case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit |
| case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read |
| case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP) |
| #if !defined(_POSIX_C_SOURCE) |
| case SIGIO: return "SIGIO"; // 23 input/output possible signal |
| #endif |
| case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit |
| case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit |
| case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm |
| case SIGPROF: return "SIGPROF"; // 27 profiling time alarm |
| #if !defined(_POSIX_C_SOURCE) |
| case SIGWINCH: return "SIGWINCH"; // 28 window size changes |
| case SIGINFO: return "SIGINFO"; // 29 information request |
| #endif |
| case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1 |
| case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2 |
| default: |
| break; |
| } |
| return NULL; |
| } |
| |
| const char * |
| Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid) |
| { |
| const char *name = ThreadNameAccessor (true, pid, tid, NULL); |
| if (name == NULL) |
| { |
| // We currently can only get the name of a thread in the current process. |
| #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 |
| if (pid == Host::GetCurrentProcessID()) |
| { |
| char pthread_name[1024]; |
| if (::pthread_getname_np (::pthread_from_mach_thread_np (tid), pthread_name, sizeof(pthread_name)) == 0) |
| { |
| if (pthread_name[0]) |
| { |
| // Set the thread in our string pool |
| ThreadNameAccessor (false, pid, tid, pthread_name); |
| // Get our copy of the thread name string |
| name = ThreadNameAccessor (true, pid, tid, NULL); |
| } |
| } |
| } |
| #endif |
| } |
| return name; |
| } |
| |
| void |
| Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name) |
| { |
| lldb::pid_t curr_pid = Host::GetCurrentProcessID(); |
| lldb::tid_t curr_tid = Host::GetCurrentThreadID(); |
| if (pid == LLDB_INVALID_PROCESS_ID) |
| pid = curr_pid; |
| |
| if (tid == LLDB_INVALID_THREAD_ID) |
| tid = curr_tid; |
| |
| #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 |
| // Set the pthread name if possible |
| if (pid == curr_pid && tid == curr_tid) |
| { |
| ::pthread_setname_np (name) == 0; |
| } |
| #endif |
| ThreadNameAccessor (false, pid, tid, name); |
| } |
| |
| FileSpec |
| Host::GetProgramFileSpec () |
| { |
| static FileSpec g_program_filepsec; |
| if (!g_program_filepsec) |
| { |
| std::string program_fullpath; |
| program_fullpath.resize (PATH_MAX); |
| // If DST is NULL, then return the number of bytes needed. |
| uint32_t len = program_fullpath.size(); |
| int err = _NSGetExecutablePath ((char *)program_fullpath.data(), &len); |
| if (err < 0) |
| { |
| // The path didn't fit in the buffer provided, increase its size |
| // and try again |
| program_fullpath.resize(len); |
| len = program_fullpath.size(); |
| err = _NSGetExecutablePath ((char *)program_fullpath.data(), &len); |
| } |
| if (err == 0) |
| g_program_filepsec.SetFile(program_fullpath.data()); |
| } |
| return g_program_filepsec; |
| } |
| |
| |
| FileSpec |
| Host::GetModuleFileSpecForHostAddress (const void *host_addr) |
| { |
| FileSpec module_filespec; |
| Dl_info info; |
| if (::dladdr (host_addr, &info)) |
| { |
| if (info.dli_fname) |
| module_filespec.SetFile(info.dli_fname); |
| } |
| return module_filespec; |
| } |
| |
| |
| bool |
| Host::ResolveExecutableInBundle (FileSpec *file) |
| { |
| if (file->GetFileType () == FileSpec::eFileTypeDirectory) |
| { |
| char path[PATH_MAX]; |
| if (file->GetPath(path, sizeof(path))) |
| { |
| CFCBundle bundle (path); |
| CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ()); |
| if (url.get()) |
| { |
| if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) |
| { |
| file->SetFile(path); |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| typedef struct MonitorInfo |
| { |
| int handle; |
| pthread_t thread; |
| Host::MonitorChildProcessCallback callback; |
| void *callback_baton; |
| bool monitor_signals; |
| }; |
| |
| typedef std::multimap<lldb::pid_t, MonitorInfo> MonitorInfoMap; |
| static pthread_mutex_t g_monitor_map_mutex = PTHREAD_MUTEX_INITIALIZER; |
| typedef lldb::SharedPtr<MonitorInfoMap>::Type MonitorInfoMapSP; |
| |
| static MonitorInfoMapSP& |
| GetMonitorMap (bool can_create) |
| { |
| static MonitorInfoMapSP g_monitor_map_sp; |
| if (can_create && g_monitor_map_sp.get() == NULL) |
| { |
| g_monitor_map_sp.reset (new MonitorInfoMap); |
| } |
| return g_monitor_map_sp; |
| } |
| |
| static Predicate<bool>& |
| GetChildProcessPredicate () |
| { |
| static Predicate<bool> g_has_child_processes; |
| return g_has_child_processes; |
| } |
| |
| static void * |
| MonitorChildProcessThreadFunction (void *arg); |
| |
| static pthread_t g_monitor_thread; |
| |
| uint32_t |
| Host::StartMonitoringChildProcess |
| ( |
| MonitorChildProcessCallback callback, |
| void *callback_baton, |
| lldb::pid_t pid, |
| bool monitor_signals |
| ) |
| { |
| static uint32_t g_handle = 0; |
| if (callback) |
| { |
| Mutex::Locker locker(&g_monitor_map_mutex); |
| if (!g_monitor_thread) |
| { |
| pid_t wait_pid = -1; |
| g_monitor_thread = ThreadCreate ("<lldb.host.wait4>", |
| MonitorChildProcessThreadFunction, |
| &wait_pid, |
| NULL); |
| if (g_monitor_thread) |
| { |
| //Host::ThreadDetach (g_monitor_thread, NULL); |
| } |
| } |
| |
| if (g_monitor_thread) |
| { |
| MonitorInfo info = { ++g_handle, 0, callback, callback_baton, monitor_signals }; |
| MonitorInfoMapSP monitor_map_sp (GetMonitorMap (true)); |
| if (monitor_map_sp) |
| { |
| monitor_map_sp->insert(std::make_pair(pid, info)); |
| GetChildProcessPredicate ().SetValue (true, eBroadcastOnChange); |
| return info.handle; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| bool |
| Host::StopMonitoringChildProcess (uint32_t handle) |
| { |
| Mutex::Locker locker(&g_monitor_map_mutex); |
| MonitorInfoMapSP monitor_map_sp (GetMonitorMap (false)); |
| if (monitor_map_sp) |
| { |
| MonitorInfoMap::iterator pos, end = monitor_map_sp->end(); |
| for (pos = monitor_map_sp->end(); pos != end; ++pos) |
| { |
| if (pos->second.handle == handle) |
| { |
| monitor_map_sp->erase(pos); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| //------------------------------------------------------------------ |
| // Scoped class that will disable thread canceling when it is |
| // constructed, and exception safely restore the previous value it |
| // when it goes out of scope. |
| //------------------------------------------------------------------ |
| class ScopedPThreadCancelDisabler |
| { |
| public: |
| |
| ScopedPThreadCancelDisabler() |
| { |
| // Disable the ability for this thread to be cancelled |
| int err = ::pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &m_old_state); |
| if (err != 0) |
| m_old_state = -1; |
| |
| } |
| |
| ~ScopedPThreadCancelDisabler() |
| { |
| // Restore the ability for this thread to be cancelled to what it |
| // previously was. |
| if (m_old_state != -1) |
| ::pthread_setcancelstate (m_old_state, 0); |
| } |
| private: |
| int m_old_state; // Save the old cancelability state. |
| }; |
| |
| |
| |
| static void * |
| MonitorChildProcessThreadFunction (void *arg) |
| { |
| Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); |
| const char *function = __FUNCTION__; |
| if (log) |
| log->Printf ("%s (arg = %p) thread starting...", function, arg); |
| |
| const pid_t wait_pid = -1;//*((pid_t*)arg); |
| int status = -1; |
| const int options = 0; |
| struct rusage *rusage = NULL; |
| while (1) |
| { |
| if (log) |
| log->Printf("%s ::wait4 (pid = %i, &status, options = %i, rusage = %p)...", function, wait_pid, options, rusage); |
| |
| // Wait for all child processes |
| ::pthread_testcancel (); |
| lldb::pid_t pid = ::wait4 (wait_pid, &status, options, rusage); |
| ::pthread_testcancel (); |
| |
| if (pid < 0) |
| { |
| // No child processes to watch wait for the mutex to be cleared |
| |
| // Scope for "locker" |
| { |
| ScopedPThreadCancelDisabler pthread_cancel_disabler; |
| |
| // First clear out all monitor entries since we have no processes |
| // to watch. |
| Mutex::Locker locker(&g_monitor_map_mutex); |
| // Since we don't have any child processes, we can safely clear |
| // anyone with a valid pid. |
| MonitorInfoMapSP monitor_map_sp(GetMonitorMap (false)); |
| if (monitor_map_sp) |
| { |
| MonitorInfoMap::iterator pos = monitor_map_sp->begin(); |
| while (pos != monitor_map_sp->end()) |
| { |
| // pid value of 0 and -1 are special (see man page on wait4...) |
| if (pos->first > 0) |
| { |
| MonitorInfoMap::iterator next_pos = pos; ++next_pos; |
| monitor_map_sp->erase (pos, next_pos); |
| pos = next_pos; |
| } |
| else |
| ++pos; |
| } |
| } |
| } |
| |
| if (log) |
| log->Printf("%s no child processes, wait for some...", function); |
| GetChildProcessPredicate ().SetValue (false, eBroadcastNever); |
| ::pthread_testcancel(); |
| GetChildProcessPredicate ().WaitForValueEqualTo (true); |
| if (log) |
| log->Printf("%s resuming monitoring of child processes.", function); |
| |
| } |
| else |
| { |
| ScopedPThreadCancelDisabler pthread_cancel_disabler; |
| bool exited = false; |
| int signal = 0; |
| int exit_status = 0; |
| const char *status_cstr = NULL; |
| if (WIFSTOPPED(status)) |
| { |
| signal = WSTOPSIG(status); |
| status_cstr = "STOPPED"; |
| } |
| else if (WIFEXITED(status)) |
| { |
| exit_status = WEXITSTATUS(status); |
| status_cstr = "EXITED"; |
| exited = true; |
| } |
| else if (WIFSIGNALED(status)) |
| { |
| signal = WTERMSIG(status); |
| status_cstr = "SIGNALED"; |
| exited = true; |
| exit_status = -1; |
| } |
| else |
| { |
| status_cstr = "(???)"; |
| } |
| |
| if (log) |
| log->Printf ("%s ::wait4 (pid = %i, &status, options = %i, rusage = %p) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_state = %i", |
| function, |
| wait_pid, |
| options, |
| rusage, |
| pid, |
| status, |
| status_cstr, |
| signal, |
| exit_status); |
| |
| // Scope for mutex locker |
| { |
| // Notify anyone listening to this process |
| Mutex::Locker locker(&g_monitor_map_mutex); |
| MonitorInfoMapSP monitor_map_sp(GetMonitorMap (false)); |
| if (monitor_map_sp) |
| { |
| std::pair<MonitorInfoMap::iterator, MonitorInfoMap::iterator> range; |
| range = monitor_map_sp->equal_range(pid); |
| MonitorInfoMap::iterator pos; |
| for (pos = range.first; pos != range.second; ++pos) |
| { |
| if (exited || (signal != 0 && pos->second.monitor_signals)) |
| { |
| bool callback_return = pos->second.callback (pos->second.callback_baton, pid, signal, exit_status); |
| |
| if (exited || callback_return) |
| { |
| // Make this entry as needing to be removed by |
| // setting its handle to zero |
| pos->second.handle = 0; |
| } |
| } |
| } |
| |
| // Remove any entries that requested to be removed or any |
| // entries for child processes that did exit. We know this |
| // because we changed the handles to an invalid value. |
| pos = monitor_map_sp->begin(); |
| while (pos != monitor_map_sp->end()) |
| { |
| if (pos->second.handle == 0) |
| { |
| MonitorInfoMap::iterator next_pos = pos; ++next_pos; |
| monitor_map_sp->erase (pos, next_pos); |
| pos = next_pos; |
| } |
| else |
| ++pos; |
| } |
| } |
| } |
| } |
| } |
| |
| if (log) |
| log->Printf ("ProcessMacOSX::%s (arg = %p) thread exiting...", __FUNCTION__, arg); |
| |
| g_monitor_thread = NULL; |
| return NULL; |
| } |
| |
| void |
| Host::WillTerminate () |
| { |
| if (g_monitor_thread != NULL) |
| { |
| ThreadCancel (g_monitor_thread, NULL); |
| GetChildProcessPredicate ().SetValue (true, eBroadcastAlways); |
| ThreadJoin(g_monitor_thread, NULL, NULL); |
| g_monitor_thread = NULL; |
| } |
| } |
| |