bool ThreadMacOSX::RestoreSuspendCount() { Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__); Error err; lldb::tid_t tid = GetID (); if (ThreadIDIsValid(tid) == false) return false; else if (m_suspend_count > m_basic_info.suspend_count) { while (m_suspend_count > m_basic_info.suspend_count) { err = ::thread_resume (tid); if (err.Success()) --m_suspend_count; if (log || err.Fail()) err.PutToLog(log, "::thread_resume (%4.4x)", tid); } } else if (m_suspend_count < m_basic_info.suspend_count) { while (m_suspend_count < m_basic_info.suspend_count) { err = ::thread_suspend (tid); if (err.Success()) --m_suspend_count; if (log || err.Fail()) err.PutToLog(log, "::thread_suspend (%4.4x)", tid); } } return m_suspend_count == m_basic_info.suspend_count; }
bool Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Error &error) { if (info == NULL) return false; posix_spawn_file_actions_t *file_actions = reinterpret_cast<posix_spawn_file_actions_t *>(_file_actions); switch (info->GetAction()) { case FileAction::eFileActionNone: error.Clear(); break; case FileAction::eFileActionClose: if (info->GetFD() == -1) error.SetErrorString("invalid fd for posix_spawn_file_actions_addclose(...)"); else { error.SetError(::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), eErrorTypePOSIX); if (log && (error.Fail() || log)) error.PutToLog(log, "posix_spawn_file_actions_addclose (action=%p, fd=%i)", static_cast<void *>(file_actions), info->GetFD()); } break; case FileAction::eFileActionDuplicate: if (info->GetFD() == -1) error.SetErrorString("invalid fd for posix_spawn_file_actions_adddup2(...)"); else if (info->GetActionArgument() == -1) error.SetErrorString("invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); else { error.SetError( ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), info->GetActionArgument()), eErrorTypePOSIX); if (log && (error.Fail() || log)) error.PutToLog(log, "posix_spawn_file_actions_adddup2 (action=%p, fd=%i, dup_fd=%i)", static_cast<void *>(file_actions), info->GetFD(), info->GetActionArgument()); } break; case FileAction::eFileActionOpen: if (info->GetFD() == -1) error.SetErrorString("invalid fd in posix_spawn_file_actions_addopen(...)"); else { int oflag = info->GetActionArgument(); mode_t mode = 0; if (oflag & O_CREAT) mode = 0640; error.SetError( ::posix_spawn_file_actions_addopen(file_actions, info->GetFD(), info->GetPath(), oflag, mode), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "posix_spawn_file_actions_addopen (action=%p, fd=%i, path='%s', oflag=%i, mode=%i)", static_cast<void *>(file_actions), info->GetFD(), info->GetPath(), oflag, mode); } break; } return error.Success(); }
Error Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid) { Error error; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); posix_spawnattr_t attr; error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); if (error.Fail()) return error; // Make a quick class that will cleanup the posix spawn attributes in case // we return in the middle of this function. lldb_utility::CleanUp <posix_spawnattr_t *, int> posix_spawnattr_cleanup(&attr, posix_spawnattr_destroy); sigset_t no_signals; sigset_t all_signals; sigemptyset (&no_signals); sigfillset (&all_signals); ::posix_spawnattr_setsigmask(&attr, &no_signals); #if defined (__linux__) || defined (__FreeBSD__) ::posix_spawnattr_setsigdefault(&attr, &no_signals); #else ::posix_spawnattr_setsigdefault(&attr, &all_signals); #endif short flags = GetPosixspawnFlags(launch_info); error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); if (error.Fail()) return error; // posix_spawnattr_setbinpref_np appears to be an Apple extension per: // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ #if defined (__APPLE__) && !defined (__arm__) // Don't set the binpref if a shell was provided. After all, that's only going to affect what version of the shell // is launched, not what fork of the binary is launched. We insert "arch --arch <ARCH> as part of the shell invocation // to do that job on OSX. if (launch_info.GetShell() == nullptr) { // We don't need to do this for ARM, and we really shouldn't now that we // have multiple CPU subtypes and no posix_spawnattr call that allows us // to set which CPU subtype to launch... const ArchSpec &arch_spec = launch_info.GetArchitecture(); cpu_type_t cpu = arch_spec.GetMachOCPUType(); cpu_type_t sub = arch_spec.GetMachOCPUSubType(); if (cpu != 0 && cpu != static_cast<cpu_type_t>(UINT32_MAX) && cpu != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE) && !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail { size_t ocount = 0; error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount); if (error.Fail() || ocount != 1) return error; } } #endif const char *tmp_argv[2]; char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector(); char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector(); if (argv == NULL) { // posix_spawn gets very unhappy if it doesn't have at least the program // name in argv[0]. One of the side affects I have noticed is the environment // variables don't make it into the child process if "argv == NULL"!!! tmp_argv[0] = exe_path; tmp_argv[1] = NULL; argv = (char * const*)tmp_argv; } #if !defined (__APPLE__) // manage the working directory char current_dir[PATH_MAX]; current_dir[0] = '\0'; #endif FileSpec working_dir{launch_info.GetWorkingDirectory()}; if (working_dir) { #if defined (__APPLE__) // Set the working directory on this thread only if (__pthread_chdir(working_dir.GetCString()) < 0) { if (errno == ENOENT) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir.GetCString()); } else if (errno == ENOTDIR) { error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir.GetCString()); } else { error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution."); } return error; } #else if (::getcwd(current_dir, sizeof(current_dir)) == NULL) { error.SetError(errno, eErrorTypePOSIX); error.LogIfError(log, "unable to save the current directory"); return error; } if (::chdir(working_dir.GetCString()) == -1) { error.SetError(errno, eErrorTypePOSIX); error.LogIfError(log, "unable to change working directory to %s", working_dir.GetCString()); return error; } #endif } ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; const size_t num_file_actions = launch_info.GetNumFileActions (); if (num_file_actions > 0) { posix_spawn_file_actions_t file_actions; error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); if (error.Fail() || log) error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); if (error.Fail()) return error; // Make a quick class that will cleanup the posix spawn attributes in case // we return in the middle of this function. lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy); for (size_t i=0; i<num_file_actions; ++i) { const FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); if (launch_file_action) { if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, error)) return error; } } error.SetError(::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail() || log) { error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", result_pid, exe_path, static_cast<void *>(&file_actions), static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), reinterpret_cast<const void *>(envp)); if (log) { for (int ii=0; argv[ii]; ++ii) log->Printf("argv[%i] = '%s'", ii, argv[ii]); } } } else { error.SetError(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail() || log) { error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )", result_pid, exe_path, static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), reinterpret_cast<const void *>(envp)); if (log) { for (int ii=0; argv[ii]; ++ii) log->Printf("argv[%i] = '%s'", ii, argv[ii]); } } } pid = result_pid; if (working_dir) { #if defined (__APPLE__) // No more thread specific current working directory __pthread_fchdir (-1); #else if (::chdir(current_dir) == -1 && error.Success()) { error.SetError(errno, eErrorTypePOSIX); error.LogIfError(log, "unable to change current directory back to %s", current_dir); } #endif } return error; }
bool OperatingSystemGo::UpdateThreadList(ThreadList &old_thread_list, ThreadList &real_thread_list, ThreadList &new_thread_list) { new_thread_list = real_thread_list; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS)); if (!(m_allg_sp || Init(real_thread_list)) || (m_allg_sp && !m_allglen_sp) || !GetGlobalPluginProperties()->GetEnableGoroutines()) { return new_thread_list.GetSize(false) > 0; } if (log) log->Printf("OperatingSystemGo::UpdateThreadList(%d, %d, %d) fetching thread data from Go for pid %" PRIu64, old_thread_list.GetSize(false), real_thread_list.GetSize(false), new_thread_list.GetSize(0), m_process->GetID()); uint64_t allglen = m_allglen_sp->GetValueAsUnsigned(0); if (allglen == 0) { return new_thread_list.GetSize(false) > 0; } std::vector<Goroutine> goroutines; // The threads that are in "new_thread_list" upon entry are the threads from the // lldb_private::Process subclass, no memory threads will be in this list. Error err; for (uint64_t i = 0; i < allglen; ++i) { goroutines.push_back(CreateGoroutineAtIndex(i, err)); if (err.Fail()) { err.PutToLog(log, "OperatingSystemGo::UpdateThreadList"); return new_thread_list.GetSize(false) > 0; } } // Make a map so we can match goroutines with backing threads. std::map<uint64_t, ThreadSP> stack_map; for (uint32_t i = 0; i < real_thread_list.GetSize(false); ++i) { ThreadSP thread = real_thread_list.GetThreadAtIndex(i, false); stack_map[thread->GetRegisterContext()->GetSP()] = thread; } for (const Goroutine &goroutine : goroutines) { if (0 /* Gidle */ == goroutine.m_status || 6 /* Gdead */ == goroutine.m_status) { continue; } ThreadSP memory_thread = old_thread_list.FindThreadByID(goroutine.m_goid, false); if (memory_thread && IsOperatingSystemPluginThread(memory_thread) && memory_thread->IsValid()) { memory_thread->ClearBackingThread(); } else { memory_thread.reset(new ThreadMemory(*m_process, goroutine.m_goid, nullptr, nullptr, goroutine.m_gobuf)); } // Search for the backing thread if the goroutine is running. if (2 == (goroutine.m_status & 0xfff)) { auto backing_it = stack_map.lower_bound(goroutine.m_lostack); if (backing_it != stack_map.end()) { if (goroutine.m_histack >= backing_it->first) { if (log) log->Printf("OperatingSystemGo::UpdateThreadList found backing thread %" PRIx64 " (%" PRIx64 ") for thread %" PRIx64 "", backing_it->second->GetID(), backing_it->second->GetProtocolID(), memory_thread->GetID()); memory_thread->SetBackingThread(backing_it->second); new_thread_list.RemoveThreadByID(backing_it->second->GetID(), false); } } } new_thread_list.AddThread(memory_thread); } return new_thread_list.GetSize(false) > 0; }