diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp index 3e3128e16d65f..26c7e727c1d7f 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp @@ -2317,14 +2317,15 @@ class CommandObjectLanguageSwiftTaskInfo final : public CommandObjectParsed { } TaskInspector task_inspector; - auto task_addr_or_err = task_inspector.GetTaskAddrFromThreadLocalStorage( - m_exe_ctx.GetThreadRef()); - if (auto error = task_addr_or_err.takeError()) { - result.AppendError(toString(std::move(error))); + std::optional maybe_task_addr = + task_inspector.GetTaskAddrFromThreadLocalStorage( + m_exe_ctx.GetThreadRef()); + if (!task_addr) { + result.AppendError("could find the task address"); return; } - task_addr = task_addr_or_err.get(); + task_addr = *maybe_task_addr; } auto ts_or_err = m_exe_ctx.GetTargetRef().GetScratchTypeSystemForLanguage( @@ -2915,19 +2916,6 @@ std::optional SwiftLanguageRuntime::TrySkipVirtualParentProlog( return pc_value; } -/// Attempts to read the memory location at `task_addr_location`, producing -/// the Task pointer if possible. -static llvm::Expected -ReadTaskAddr(lldb::addr_t task_addr_location, Process &process) { - Status status; - addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status); - if (status.Fail()) - return llvm::joinErrors( - llvm::createStringError("could not get current task from thread"), - status.takeError()); - return task_addr; -} - /// Compute the location where the Task pointer for `real_thread` is stored by /// the runtime. static llvm::Expected @@ -2955,48 +2943,154 @@ ComputeTaskAddrLocationFromThreadLocalStorage(Thread &real_thread) { #endif } -llvm::Expected +/// Helper function to read all `pointers` from process memory at once. +/// Consumes any errors from the input by propagating them to the output. +static llvm::SmallVector> +MultiReadPointers(Process &process, + llvm::MutableArrayRef> maybe_pointers) { + llvm::SmallVector> final_results; + llvm::SmallVector to_read; + final_results.reserve(maybe_pointers.size()); + to_read.reserve(maybe_pointers.size()); + + /// Filter the input: propagate input errors directly to the output, forward + /// proper inputs to `to_read`. + for (std::optional &maybe_ptr : maybe_pointers) { + if (!maybe_ptr) + final_results.emplace_back(std::nullopt); + else { + final_results.push_back(LLDB_INVALID_ADDRESS); + to_read.push_back(*maybe_ptr); + } + } + + /// TODO: convert this loop into a call to the vectorized memory read, once + /// that is available in Process. + llvm::SmallVector> read_results; + for (addr_t pointer : to_read) { + Status status; + addr_t result = process.ReadPointerFromMemory(pointer, status); + if (status.Fail()) + read_results.push_back(std::nullopt); + else + read_results.push_back(result); + } + + llvm::MutableArrayRef> results_ref = read_results; + + // Move the results in the slots not filled by errors from the input. + for (std::optional &maybe_result : final_results) + if (maybe_result) + maybe_result = results_ref.consume_front(); + + assert(results_ref.empty()); + return final_results; +} + +/// Helper function to read `addr` from process memory. Errors in the input are +/// propagated to to the output. +static std::optional ReadPointer(Process &process, + std::optional addr) { + return MultiReadPointers(process, addr)[0]; +} + +std::optional TaskInspector::GetTaskAddrFromThreadLocalStorage(Thread &thread) { - // Look through backing threads when inspecting TLS. - Thread &real_thread = - thread.GetBackingThread() ? *thread.GetBackingThread() : thread; + return GetTaskAddrFromThreadLocalStorage(&thread)[0]; +} + +llvm::SmallVector> +TaskInspector::GetTaskAddrLocations(llvm::ArrayRef threads) { + llvm::SmallVector> addr_locations; + addr_locations.reserve(threads.size()); - if (auto it = m_tid_to_task_addr_location.find(real_thread.GetID()); - it != m_tid_to_task_addr_location.end()) { + for (auto [idx, thread] : llvm::enumerate(threads)) { + Thread &real_thread = + thread->GetBackingThread() ? *thread->GetBackingThread() : *thread; + + auto it = m_tid_to_task_addr_location.find(real_thread.GetID()); + if (it != m_tid_to_task_addr_location.end()) { + addr_locations.push_back(it->second); #ifndef NDEBUG - // In assert builds, check that caching did not produce incorrect results. - llvm::Expected task_addr_location = - ComputeTaskAddrLocationFromThreadLocalStorage(real_thread); - assert(task_addr_location); - assert(it->second == *task_addr_location); + // In assert builds, check that caching did not produce incorrect results. + llvm::Expected task_addr_location = + ComputeTaskAddrLocationFromThreadLocalStorage(real_thread); + assert(task_addr_location); + assert(it->second == *task_addr_location); #endif - llvm::Expected task_addr = - ReadTaskAddr(it->second, *thread.GetProcess()); - if (task_addr) - return task_addr; - // If the cached task addr location became invalid, invalidate the cache. - m_tid_to_task_addr_location.erase(it); - LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(), - "TaskInspector: evicted task location address due to " - "invalid memory read: {0}"); - } - - llvm::Expected task_addr_location = + continue; + } + llvm::Expected addr_loc = + ComputeTaskAddrLocationFromThreadLocalStorage(real_thread); + if (!addr_loc) { + LLDB_LOG_ERROR(GetLog(LLDBLog::OS), addr_loc.takeError(), + "TaskInspector: failed to compute task address location " + "from TLS: {0}"); + addr_locations.push_back(std::nullopt); + } else + addr_locations.push_back(*addr_loc); + } + return addr_locations; +} + +std::optional TaskInspector::RetryRead(Thread &thread, + addr_t task_addr_location) { + Thread &real_thread = + thread.GetBackingThread() ? *thread.GetBackingThread() : thread; + user_id_t tid = real_thread.GetID(); + + // For unsuccessful reads whose address was not cached, don't try again. + if (!m_tid_to_task_addr_location.erase(tid)) + return std::nullopt; + + LLDB_LOG(GetLog(LLDBLog::OS), "TaskInspector: evicted task location " + "address due to invalid memory read"); + + // The cached address could not be loaded. "This should never happen", but + // recompute the address and try again for completeness. + llvm::Expected task_addr_loc = ComputeTaskAddrLocationFromThreadLocalStorage(real_thread); - if (!task_addr_location) - return task_addr_location; - - llvm::Expected task_addr = - ReadTaskAddr(*task_addr_location, *thread.GetProcess()); - - // If the read from this TLS address is successful, cache the TLS address. - // Caching without a valid read is dangerous: earlier in the thread - // lifetime, the result of GetExtendedInfo can be invalid. - if (task_addr && - real_thread.GetProcess()->GetTarget().GetSwiftCacheTaskPointerLocation()) - m_tid_to_task_addr_location.try_emplace(real_thread.GetID(), - *task_addr_location); - return task_addr; + if (!task_addr_loc) { + LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr_loc.takeError(), + "TaskInspector: failed to compute task address location " + "from TLS: {0}"); + return std::nullopt; + } + + std::optional read_retry_result = + ReadPointer(*thread.GetProcess(), *task_addr_loc); + if (read_retry_result) + m_tid_to_task_addr_location[tid] = *task_addr_loc; + return read_retry_result; +} + +llvm::SmallVector> +TaskInspector::GetTaskAddrFromThreadLocalStorage( + llvm::ArrayRef threads) { + if (threads.empty()) + return {}; + + llvm::SmallVector> addr_locations = + GetTaskAddrLocations(threads); + + Process &process = *threads[0]->GetProcess(); + llvm::SmallVector> mem_read_results = + MultiReadPointers(process, addr_locations); + + for (auto [idx, thread] : llvm::enumerate(threads)) { + if (!addr_locations[idx]) + continue; + // If the read was successful, cache the address. + if (mem_read_results[idx]) { + Thread &real_thread = + thread->GetBackingThread() ? *thread->GetBackingThread() : *thread; + m_tid_to_task_addr_location[real_thread.GetID()] = *addr_locations[idx]; + continue; + } + mem_read_results[idx] = RetryRead(*thread, *addr_locations[idx]); + } + + return mem_read_results; } namespace { diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h index b9c0a570d1e96..40806c9378e7d 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h @@ -918,10 +918,24 @@ class TaskInspector { public: /// Inspects thread local storage to find the address of the currently /// executing task, if any. - llvm::Expected - GetTaskAddrFromThreadLocalStorage(Thread &thread); + std::optional GetTaskAddrFromThreadLocalStorage(Thread &thread); + + /// Inspects thread local storage to find the address of the currently + /// executing task, if any. + llvm::SmallVector> + GetTaskAddrFromThreadLocalStorage(llvm::ArrayRef threads); private: + /// For each thread in `threads`, return the location of the its task + /// pointer, if it exists. + llvm::SmallVector> + GetTaskAddrLocations(llvm::ArrayRef threads); + + /// If reading from a cached task address location failed, invalidate the + /// cache and try again. + std::optional RetryRead(Thread &thread, + lldb::addr_t task_addr_location); + llvm::DenseMap m_tid_to_task_addr_location; }; diff --git a/lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.cpp b/lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.cpp index 08d220e333b01..426b2ab54c2e4 100644 --- a/lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.cpp +++ b/lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.cpp @@ -151,19 +151,32 @@ OperatingSystemSwiftTasks::FindOrCreateSwiftThread(ThreadList &old_thread_list, /*register_data_addr*/ 0); } -static std::optional FindTaskAddress(TaskInspector &task_inspector, - Thread &thread) { - llvm::Expected task_addr = - task_inspector.GetTaskAddrFromThreadLocalStorage(thread); - if (!task_addr) { - LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(), - "OperatingSystemSwiftTasks: failed to find task address in " - "thread local storage: {0}"); - return {}; +/// For each thread in `threads_it`, computes the task address that is being run +/// by the thread, if any. +static llvm::SmallVector> +FindTaskAddresses(TaskInspector &task_inspector, + ThreadCollection::ThreadIterable &threads_it) { + llvm::SmallVector threads; + for (const ThreadSP &thread : threads_it) + threads.push_back(thread.get()); + + llvm::SmallVector> task_addrs; + task_addrs.reserve(threads.size()); + + for (std::optional &task_addr : + task_inspector.GetTaskAddrFromThreadLocalStorage(threads)) { + if (!task_addr) { + LLDB_LOG(GetLog(LLDBLog::OS), "OperatingSystemSwiftTasks: failed to find " + "task address in thread local storage"); + task_addrs.push_back(std::nullopt); + continue; + } + if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS) + task_addrs.push_back(std::nullopt); + else + task_addrs.push_back(*task_addr); } - if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS) - return std::nullopt; - return *task_addr; + return task_addrs; } static std::optional FindTaskId(addr_t task_addr, Process &process) { @@ -189,10 +202,13 @@ bool OperatingSystemSwiftTasks::UpdateThreadList(ThreadList &old_thread_list, Log *log = GetLog(LLDBLog::OS); LLDB_LOG(log, "OperatingSystemSwiftTasks: Updating thread list"); - for (const ThreadSP &real_thread : core_thread_list.Threads()) { - std::optional task_addr = - FindTaskAddress(m_task_inspector, *real_thread); + ThreadCollection::ThreadIterable locked_core_threads = + core_thread_list.Threads(); + llvm::SmallVector> task_addrs = + FindTaskAddresses(m_task_inspector, locked_core_threads); + for (const auto &[real_thread, task_addr] : + llvm::zip(locked_core_threads, task_addrs)) { // If this is not a thread running a Task, add it to the list as is. if (!task_addr) { new_thread_list.AddThread(real_thread);