Skip to content

Commit 306407b

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 306407b

File tree

4 files changed

+87
-20
lines changed

4 files changed

+87
-20
lines changed

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

Lines changed: 65 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,30 @@ 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+
/// Attempts to read the memory location at `task_addr_location`, producing
2944+
/// the Task pointer if possible.
2945+
static llvm::Expected<lldb::addr_t>
2946+
ReadTaskAddr(lldb::addr_t task_addr_location, Process &process) {
2947+
Status status;
2948+
addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status);
2949+
if (status.Fail())
2950+
return llvm::joinErrors(
2951+
llvm::createStringError("could not get current task from thread"),
2952+
status.takeError());
2953+
return task_addr;
2954+
}
2955+
2956+
/// Compute the location where the Task pointer for `real_thread` is stored by
2957+
/// the runtime.
2958+
static llvm::Expected<lldb::addr_t>
2959+
ComputeTaskAddrLocationFromThreadLocalStorage(Thread &real_thread) {
29432960
#if !SWIFT_THREADING_USE_RESERVED_TLS_KEYS
29442961
return llvm::createStringError(
29452962
"getting the current task from a thread is not supported");
29462963
#else
29472964
// Compute the thread local storage address for this thread.
29482965
addr_t tsd_addr = LLDB_INVALID_ADDRESS;
29492966

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

29602974
// Offset of the Task pointer in a Thread's local storage.
2961-
Process &process = *thread.GetProcess();
2975+
Process &process = *real_thread.GetProcess();
29622976
size_t ptr_size = process.GetAddressByteSize();
29632977
uint64_t task_ptr_offset_in_tls =
29642978
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;
2979+
return tsd_addr + task_ptr_offset_in_tls;
2980+
#endif
2981+
}
2982+
2983+
llvm::Expected<lldb::addr_t>
2984+
TaskInspector::GetTaskAddrFromThreadLocalStorage(Thread &thread) {
2985+
// Look through backing threads when inspecting TLS.
2986+
Thread &real_thread =
2987+
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
2988+
2989+
if (auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
2990+
it != m_tid_to_task_addr_location.end()) {
2991+
#ifndef NDEBUG
2992+
// In assert builds, check that caching did not produce incorrect results.
2993+
llvm::Expected<lldb::addr_t> task_addr_location =
2994+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
2995+
assert(task_addr_location);
2996+
assert(it->second == *task_addr_location);
29722997
#endif
2998+
llvm::Expected<lldb::addr_t> task_addr =
2999+
ReadTaskAddr(it->second, *thread.GetProcess());
3000+
if (task_addr)
3001+
return task_addr;
3002+
// If the cached task addr location became invalid, invalidate the cache.
3003+
m_tid_to_task_addr_location.erase(it);
3004+
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(),
3005+
"TaskInspector: evicted task location address due to "
3006+
"invalid memory read: {0}");
3007+
}
3008+
3009+
llvm::Expected<lldb::addr_t> task_addr_location =
3010+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
3011+
if (!task_addr_location)
3012+
return task_addr_location;
3013+
3014+
llvm::Expected<lldb::addr_t> task_addr =
3015+
ReadTaskAddr(*task_addr_location, *thread.GetProcess());
3016+
3017+
// If the read from this TLS address is successful, cache the TLS address.
3018+
// Caching without a valid read is dangerous: earlier in the thread
3019+
// lifetime, the result of GetExtendedInfo can be invalid.
3020+
if (task_addr)
3021+
m_tid_to_task_addr_location.try_emplace(real_thread.GetID(),
3022+
*task_addr_location);
3023+
return task_addr;
29733024
}
29743025

29753026
namespace {

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,17 @@ 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+
llvm::DenseMap<uint64_t, lldb::addr_t> m_tid_to_task_addr_location;
915+
};
908916

909917
llvm::Expected<std::optional<std::string>> GetTaskName(lldb::addr_t task,
910918
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)