Skip to content

Commit bc54055

Browse files
[lldb][swift] Cache location of Task Pointers for OperatingSystemSwiftTasks
Obtaining the TLS from debugserver over a wired (or slower) conection was identified as a major source of slowdown after introducing OperatingSystemSwiftTasks. This is due to multiple memory reads being necessary: 1. Memory reads to obtain auxiliary information from libpthread that is then sent to the jThreadExtendedInfo packet. See "plo_pthread_tsd_base_address_offset","plo_pthread_tsd_base_offset" and "plo_pthread_tsd_entry_size". 2. A packet requesting jThreadExtendedInfo. Both 1 and 2 are done per thread, on every stop. However, there is strong evidence that we can safely cache the TLS pointer after a certain point in the thread lifetime, which is what this patch accomplishes. Caching is done at the OperatingSystemSwiftTasks level, not at the core Thread class. This is done for two reasons: 1. To minimize the surface area / risk of this change. 2. jThreadExtendedInfo can produce an invalid TLS address earlier in the thread lifetime, so we can only cache this if a memory read at that location is successful. On local testing over a wired connection, this reduces stepping time by about 100ms, from an average of 300ms.
1 parent 7808380 commit bc54055

File tree

4 files changed

+81
-20
lines changed

4 files changed

+81
-20
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,8 +2325,9 @@ class CommandObjectLanguageSwiftTaskInfo final : public CommandObjectParsed {
23252325
return;
23262326
}
23272327

2328-
auto task_addr_or_err =
2329-
GetTaskAddrFromThreadLocalStorage(m_exe_ctx.GetThreadRef());
2328+
TaskInspector task_inspector;
2329+
auto task_addr_or_err = task_inspector.GetTaskAddrFromThreadLocalStorage(
2330+
m_exe_ctx.GetThreadRef());
23302331
if (auto error = task_addr_or_err.takeError()) {
23312332
result.AppendError(toString(std::move(error)));
23322333
return;
@@ -2939,17 +2940,27 @@ std::optional<lldb::addr_t> SwiftLanguageRuntime::TrySkipVirtualParentProlog(
29392940
return pc_value + prologue_size;
29402941
}
29412942

2942-
llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread) {
2943+
llvm::Expected<lldb::addr_t>
2944+
TaskInspector::ReadTaskAddr(lldb::addr_t task_addr_location, Process &process) {
2945+
Status status;
2946+
addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status);
2947+
if (status.Fail())
2948+
return llvm::createStringError("could not get current task from thread: %s",
2949+
status.AsCString());
2950+
return task_addr;
2951+
}
2952+
2953+
/// Compute the location where the Task pointer for `real_thread` is stored by
2954+
/// the runtime.
2955+
static llvm::Expected<lldb::addr_t>
2956+
ComputeTaskAddrFromThreadLocalStorage(Thread &real_thread) {
29432957
#if !SWIFT_THREADING_USE_RESERVED_TLS_KEYS
29442958
return llvm::createStringError(
29452959
"getting the current task from a thread is not supported");
29462960
#else
29472961
// Compute the thread local storage address for this thread.
29482962
addr_t tsd_addr = LLDB_INVALID_ADDRESS;
29492963

2950-
// Look through backing threads when inspecting TLS.
2951-
Thread &real_thread =
2952-
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
29532964
if (auto info_sp = real_thread.GetExtendedInfo())
29542965
if (auto *info_dict = info_sp->GetAsDictionary())
29552966
info_dict->GetValueForKeyAsInteger("tsd_address", tsd_addr);
@@ -2958,18 +2969,47 @@ llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread) {
29582969
return llvm::createStringError("could not read current task from thread");
29592970

29602971
// Offset of the Task pointer in a Thread's local storage.
2961-
Process &process = *thread.GetProcess();
2972+
Process &process = *real_thread.GetProcess();
29622973
size_t ptr_size = process.GetAddressByteSize();
29632974
uint64_t task_ptr_offset_in_tls =
29642975
swift::tls_get_key(swift::tls_key::concurrency_task) * ptr_size;
2965-
addr_t task_addr_location = tsd_addr + task_ptr_offset_in_tls;
2966-
Status status;
2967-
addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status);
2968-
if (status.Fail())
2969-
return llvm::createStringError("could not get current task from thread: %s",
2970-
status.AsCString());
2971-
return task_addr;
2976+
return tsd_addr + task_ptr_offset_in_tls;
2977+
#endif
2978+
}
2979+
2980+
llvm::Expected<lldb::addr_t>
2981+
TaskInspector::GetTaskAddrFromThreadLocalStorage(Thread &thread) {
2982+
// Look through backing threads when inspecting TLS.
2983+
Thread &real_thread =
2984+
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
2985+
2986+
if (auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
2987+
it != m_tid_to_task_addr_location.end()) {
2988+
#ifndef NDEBUG
2989+
// In assert builds, check that caching did not produce incorrect results.
2990+
llvm::Expected<lldb::addr_t> actual_addr =
2991+
ComputeTaskAddrFromThreadLocalStorage(real_thread);
2992+
assert(actual_addr);
2993+
assert(it->second == *actual_addr);
29722994
#endif
2995+
return ReadTaskAddr(it->second, *thread.GetProcess());
2996+
}
2997+
2998+
llvm::Expected<lldb::addr_t> task_addr_location =
2999+
ComputeTaskAddrFromThreadLocalStorage(real_thread);
3000+
if (!task_addr_location)
3001+
return task_addr_location;
3002+
3003+
llvm::Expected<lldb::addr_t> task_addr =
3004+
ReadTaskAddr(*task_addr_location, *thread.GetProcess());
3005+
3006+
// If the read from this TLS address is successful, cache the TLS address.
3007+
// Caching without a valid read is dangerous: earlier in the thread
3008+
// lifetime, the result of GetExtendedInfo can be invalid.
3009+
if (task_addr)
3010+
m_tid_to_task_addr_location.try_emplace(real_thread.GetID(),
3011+
*task_addr_location);
3012+
return task_addr;
29733013
}
29743014

29753015
namespace {

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,22 @@ struct AsyncUnwindRegisterNumbers {
902902
std::optional<AsyncUnwindRegisterNumbers>
903903
GetAsyncUnwindRegisterNumbers(llvm::Triple::ArchType triple);
904904

905-
/// Inspects thread local storage to find the address of the currently executing
906-
/// task.
907-
llvm::Expected<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread);
905+
/// A helper class to find and cache the location of Task pointer inside TLS.
906+
class TaskInspector {
907+
public:
908+
/// Inspects thread local storage to find the address of the currently
909+
/// executing task, if any.
910+
llvm::Expected<lldb::addr_t>
911+
GetTaskAddrFromThreadLocalStorage(Thread &thread);
912+
913+
private:
914+
/// Attempts to read the memory location at `task_addr_location`, producing
915+
/// the Task pointer if possible.
916+
llvm::Expected<lldb::addr_t> ReadTaskAddr(lldb::addr_t task_addr_location,
917+
Process &process);
918+
919+
llvm::DenseMap<uint64_t, lldb::addr_t> m_tid_to_task_addr_location;
920+
};
908921

909922
llvm::Expected<std::optional<std::string>> GetTaskName(lldb::addr_t task,
910923
Process &process);

lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ ThreadSP OperatingSystemSwiftTasks::FindOrCreateSwiftThread(
100100
name);
101101
}
102102

103-
static std::optional<addr_t> FindTaskAddress(Thread &thread) {
104-
llvm::Expected<addr_t> task_addr = GetTaskAddrFromThreadLocalStorage(thread);
103+
static std::optional<addr_t> FindTaskAddress(TaskInspector &task_inspector,
104+
Thread &thread) {
105+
llvm::Expected<addr_t> task_addr =
106+
task_inspector.GetTaskAddrFromThreadLocalStorage(thread);
105107
if (!task_addr) {
106108
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(),
107109
"OperatingSystemSwiftTasks: failed to find task address in "
@@ -150,7 +152,8 @@ bool OperatingSystemSwiftTasks::UpdateThreadList(ThreadList &old_thread_list,
150152
LLDB_LOG(log, "OperatingSystemSwiftTasks: Updating thread list");
151153

152154
for (const ThreadSP &real_thread : core_thread_list.Threads()) {
153-
std::optional<addr_t> task_addr = FindTaskAddress(*real_thread);
155+
std::optional<addr_t> task_addr =
156+
FindTaskAddress(m_task_inspector, *real_thread);
154157

155158
// If this is not a thread running a Task, add it to the list as is.
156159
if (!task_addr) {

lldb/source/Plugins/OperatingSystem/SwiftTasks/OperatingSystemSwiftTasks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#if LLDB_ENABLE_SWIFT
1313

14+
#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
1415
#include "lldb/Target/OperatingSystem.h"
1516

1617
namespace lldb_private {
@@ -51,6 +52,10 @@ class OperatingSystemSwiftTasks : public OperatingSystem {
5152
lldb::ThreadSP FindOrCreateSwiftThread(ThreadList &old_thread_list,
5253
uint64_t task_id,
5354
std::optional<std::string> task_name);
55+
56+
/// A cache for task addr locations, which are expensive to compute but
57+
/// immutable.
58+
TaskInspector m_task_inspector;
5459
};
5560
} // namespace lldb_private
5661

0 commit comments

Comments
 (0)