Skip to content

Commit fb7c49e

Browse files
[lldb][NFC] Prepare SwiftLanguageRuntime for vectorized memory reads
Once the proposal [1] is implemented, the Process class will have the ability to read multiple memory addresses at once. This patch is an NFC refactor to make the code more amenable to that future. [1]: https://discourse.llvm.org/t/rfc-a-new-vectorized-memory-read-packet/
1 parent 088ed7e commit fb7c49e

File tree

3 files changed

+196
-72
lines changed

3 files changed

+196
-72
lines changed

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

Lines changed: 149 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,14 +2317,15 @@ class CommandObjectLanguageSwiftTaskInfo final : public CommandObjectParsed {
23172317
}
23182318

23192319
TaskInspector task_inspector;
2320-
auto task_addr_or_err = task_inspector.GetTaskAddrFromThreadLocalStorage(
2321-
m_exe_ctx.GetThreadRef());
2322-
if (auto error = task_addr_or_err.takeError()) {
2323-
result.AppendError(toString(std::move(error)));
2320+
std::optional<lldb::addr_t> maybe_task_addr =
2321+
task_inspector.GetTaskAddrFromThreadLocalStorage(
2322+
m_exe_ctx.GetThreadRef());
2323+
if (!task_addr) {
2324+
result.AppendError("could find the task address");
23242325
return;
23252326
}
23262327

2327-
task_addr = task_addr_or_err.get();
2328+
task_addr = *maybe_task_addr;
23282329
}
23292330

23302331
auto ts_or_err = m_exe_ctx.GetTargetRef().GetScratchTypeSystemForLanguage(
@@ -2915,19 +2916,6 @@ std::optional<lldb::addr_t> SwiftLanguageRuntime::TrySkipVirtualParentProlog(
29152916
return pc_value;
29162917
}
29172918

2918-
/// Attempts to read the memory location at `task_addr_location`, producing
2919-
/// the Task pointer if possible.
2920-
static llvm::Expected<lldb::addr_t>
2921-
ReadTaskAddr(lldb::addr_t task_addr_location, Process &process) {
2922-
Status status;
2923-
addr_t task_addr = process.ReadPointerFromMemory(task_addr_location, status);
2924-
if (status.Fail())
2925-
return llvm::joinErrors(
2926-
llvm::createStringError("could not get current task from thread"),
2927-
status.takeError());
2928-
return task_addr;
2929-
}
2930-
29312919
/// Compute the location where the Task pointer for `real_thread` is stored by
29322920
/// the runtime.
29332921
static llvm::Expected<lldb::addr_t>
@@ -2955,48 +2943,154 @@ ComputeTaskAddrLocationFromThreadLocalStorage(Thread &real_thread) {
29552943
#endif
29562944
}
29572945

2958-
llvm::Expected<lldb::addr_t>
2946+
/// Helper function to read all `pointers` from process memory at once.
2947+
/// Consumes any errors from the input by propagating them to the output.
2948+
static llvm::SmallVector<std::optional<addr_t>>
2949+
MultiReadPointers(Process &process,
2950+
llvm::MutableArrayRef<std::optional<addr_t>> maybe_pointers) {
2951+
llvm::SmallVector<std::optional<addr_t>> final_results;
2952+
llvm::SmallVector<addr_t> to_read;
2953+
final_results.reserve(maybe_pointers.size());
2954+
to_read.reserve(maybe_pointers.size());
2955+
2956+
/// Filter the input: propagate input errors directly to the output, forward
2957+
/// proper inputs to `to_read`.
2958+
for (std::optional<addr_t> &maybe_ptr : maybe_pointers) {
2959+
if (!maybe_ptr)
2960+
final_results.emplace_back(std::nullopt);
2961+
else {
2962+
final_results.push_back(LLDB_INVALID_ADDRESS);
2963+
to_read.push_back(*maybe_ptr);
2964+
}
2965+
}
2966+
2967+
/// TODO: convert this loop into a call to the vectorized memory read, once
2968+
/// that is available in Process.
2969+
llvm::SmallVector<std::optional<addr_t>> read_results;
2970+
for (addr_t pointer : to_read) {
2971+
Status status;
2972+
addr_t result = process.ReadPointerFromMemory(pointer, status);
2973+
if (status.Fail())
2974+
read_results.push_back(std::nullopt);
2975+
else
2976+
read_results.push_back(result);
2977+
}
2978+
2979+
llvm::MutableArrayRef<std::optional<addr_t>> results_ref = read_results;
2980+
2981+
// Move the results in the slots not filled by errors from the input.
2982+
for (std::optional<addr_t> &maybe_result : final_results)
2983+
if (maybe_result)
2984+
maybe_result = results_ref.consume_front();
2985+
2986+
assert(results_ref.empty());
2987+
return final_results;
2988+
}
2989+
2990+
/// Helper function to read `addr` from process memory. Errors in the input are
2991+
/// propagated to to the output.
2992+
static std::optional<addr_t> ReadPointer(Process &process,
2993+
std::optional<addr_t> addr) {
2994+
return MultiReadPointers(process, addr)[0];
2995+
}
2996+
2997+
std::optional<lldb::addr_t>
29592998
TaskInspector::GetTaskAddrFromThreadLocalStorage(Thread &thread) {
2960-
// Look through backing threads when inspecting TLS.
2961-
Thread &real_thread =
2962-
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
2999+
return GetTaskAddrFromThreadLocalStorage(&thread)[0];
3000+
}
3001+
3002+
llvm::SmallVector<std::optional<lldb::addr_t>>
3003+
TaskInspector::GetTaskAddrLocations(llvm::ArrayRef<Thread *> threads) {
3004+
llvm::SmallVector<std::optional<addr_t>> addr_locations;
3005+
addr_locations.reserve(threads.size());
29633006

2964-
if (auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
2965-
it != m_tid_to_task_addr_location.end()) {
3007+
for (auto [idx, thread] : llvm::enumerate(threads)) {
3008+
Thread &real_thread =
3009+
thread->GetBackingThread() ? *thread->GetBackingThread() : *thread;
3010+
3011+
auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
3012+
if (it != m_tid_to_task_addr_location.end()) {
3013+
addr_locations.push_back(it->second);
29663014
#ifndef NDEBUG
2967-
// In assert builds, check that caching did not produce incorrect results.
2968-
llvm::Expected<lldb::addr_t> task_addr_location =
2969-
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
2970-
assert(task_addr_location);
2971-
assert(it->second == *task_addr_location);
3015+
// In assert builds, check that caching did not produce incorrect results.
3016+
llvm::Expected<lldb::addr_t> task_addr_location =
3017+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
3018+
assert(task_addr_location);
3019+
assert(it->second == *task_addr_location);
29723020
#endif
2973-
llvm::Expected<lldb::addr_t> task_addr =
2974-
ReadTaskAddr(it->second, *thread.GetProcess());
2975-
if (task_addr)
2976-
return task_addr;
2977-
// If the cached task addr location became invalid, invalidate the cache.
2978-
m_tid_to_task_addr_location.erase(it);
2979-
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(),
2980-
"TaskInspector: evicted task location address due to "
2981-
"invalid memory read: {0}");
2982-
}
2983-
2984-
llvm::Expected<lldb::addr_t> task_addr_location =
3021+
continue;
3022+
}
3023+
llvm::Expected<addr_t> addr_loc =
3024+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
3025+
if (!addr_loc) {
3026+
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), addr_loc.takeError(),
3027+
"TaskInspector: failed to compute task address location "
3028+
"from TLS: {0}");
3029+
addr_locations.push_back(std::nullopt);
3030+
} else
3031+
addr_locations.push_back(*addr_loc);
3032+
}
3033+
return addr_locations;
3034+
}
3035+
3036+
std::optional<addr_t> TaskInspector::RetryRead(Thread &thread,
3037+
addr_t task_addr_location) {
3038+
Thread &real_thread =
3039+
thread.GetBackingThread() ? *thread.GetBackingThread() : thread;
3040+
user_id_t tid = real_thread.GetID();
3041+
3042+
// For unsuccessful reads whose address was not cached, don't try again.
3043+
if (!m_tid_to_task_addr_location.erase(tid))
3044+
return std::nullopt;
3045+
3046+
LLDB_LOG(GetLog(LLDBLog::OS), "TaskInspector: evicted task location "
3047+
"address due to invalid memory read");
3048+
3049+
// The cached address could not be loaded. "This should never happen", but
3050+
// recompute the address and try again for completeness.
3051+
llvm::Expected<addr_t> task_addr_loc =
29853052
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
2986-
if (!task_addr_location)
2987-
return task_addr_location;
2988-
2989-
llvm::Expected<lldb::addr_t> task_addr =
2990-
ReadTaskAddr(*task_addr_location, *thread.GetProcess());
2991-
2992-
// If the read from this TLS address is successful, cache the TLS address.
2993-
// Caching without a valid read is dangerous: earlier in the thread
2994-
// lifetime, the result of GetExtendedInfo can be invalid.
2995-
if (task_addr &&
2996-
real_thread.GetProcess()->GetTarget().GetSwiftCacheTaskPointerLocation())
2997-
m_tid_to_task_addr_location.try_emplace(real_thread.GetID(),
2998-
*task_addr_location);
2999-
return task_addr;
3053+
if (!task_addr_loc) {
3054+
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr_loc.takeError(),
3055+
"TaskInspector: failed to compute task address location "
3056+
"from TLS: {0}");
3057+
return std::nullopt;
3058+
}
3059+
3060+
std::optional<addr_t> read_retry_result =
3061+
ReadPointer(*thread.GetProcess(), *task_addr_loc);
3062+
if (read_retry_result)
3063+
m_tid_to_task_addr_location[tid] = *task_addr_loc;
3064+
return read_retry_result;
3065+
}
3066+
3067+
llvm::SmallVector<std::optional<addr_t>>
3068+
TaskInspector::GetTaskAddrFromThreadLocalStorage(
3069+
llvm::ArrayRef<Thread *> threads) {
3070+
if (threads.empty())
3071+
return {};
3072+
3073+
llvm::SmallVector<std::optional<addr_t>> addr_locations =
3074+
GetTaskAddrLocations(threads);
3075+
3076+
Process &process = *threads[0]->GetProcess();
3077+
llvm::SmallVector<std::optional<addr_t>> mem_read_results =
3078+
MultiReadPointers(process, addr_locations);
3079+
3080+
for (auto [idx, thread] : llvm::enumerate(threads)) {
3081+
if (!addr_locations[idx])
3082+
continue;
3083+
// If the read was successful, cache the address.
3084+
if (mem_read_results[idx]) {
3085+
Thread &real_thread =
3086+
thread->GetBackingThread() ? *thread->GetBackingThread() : *thread;
3087+
m_tid_to_task_addr_location[real_thread.GetID()] = *addr_locations[idx];
3088+
continue;
3089+
}
3090+
mem_read_results[idx] = RetryRead(*thread, *addr_locations[idx]);
3091+
}
3092+
3093+
return mem_read_results;
30003094
}
30013095

30023096
namespace {

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,10 +918,24 @@ class TaskInspector {
918918
public:
919919
/// Inspects thread local storage to find the address of the currently
920920
/// executing task, if any.
921-
llvm::Expected<lldb::addr_t>
922-
GetTaskAddrFromThreadLocalStorage(Thread &thread);
921+
std::optional<lldb::addr_t> GetTaskAddrFromThreadLocalStorage(Thread &thread);
922+
923+
/// Inspects thread local storage to find the address of the currently
924+
/// executing task, if any.
925+
llvm::SmallVector<std::optional<lldb::addr_t>>
926+
GetTaskAddrFromThreadLocalStorage(llvm::ArrayRef<Thread *> threads);
923927

924928
private:
929+
/// For each thread in `threads`, return the location of the its task
930+
/// pointer, if it exists.
931+
llvm::SmallVector<std::optional<lldb::addr_t>>
932+
GetTaskAddrLocations(llvm::ArrayRef<Thread *> threads);
933+
934+
/// If reading from a cached task address location failed, invalidate the
935+
/// cache and try again.
936+
std::optional<lldb::addr_t> RetryRead(Thread &thread,
937+
lldb::addr_t task_addr_location);
938+
925939
llvm::DenseMap<uint64_t, lldb::addr_t> m_tid_to_task_addr_location;
926940
};
927941

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,32 @@ OperatingSystemSwiftTasks::FindOrCreateSwiftThread(ThreadList &old_thread_list,
151151
/*register_data_addr*/ 0);
152152
}
153153

154-
static std::optional<addr_t> FindTaskAddress(TaskInspector &task_inspector,
155-
Thread &thread) {
156-
llvm::Expected<addr_t> task_addr =
157-
task_inspector.GetTaskAddrFromThreadLocalStorage(thread);
158-
if (!task_addr) {
159-
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), task_addr.takeError(),
160-
"OperatingSystemSwiftTasks: failed to find task address in "
161-
"thread local storage: {0}");
162-
return {};
154+
/// For each thread in `threads_it`, computes the task address that is being run
155+
/// by the thread, if any.
156+
static llvm::SmallVector<std::optional<addr_t>>
157+
FindTaskAddresses(TaskInspector &task_inspector,
158+
ThreadCollection::ThreadIterable &threads_it) {
159+
llvm::SmallVector<Thread *> threads;
160+
for (const ThreadSP &thread : threads_it)
161+
threads.push_back(thread.get());
162+
163+
llvm::SmallVector<std::optional<addr_t>> task_addrs;
164+
task_addrs.reserve(threads.size());
165+
166+
for (std::optional<addr_t> &task_addr :
167+
task_inspector.GetTaskAddrFromThreadLocalStorage(threads)) {
168+
if (!task_addr) {
169+
LLDB_LOG(GetLog(LLDBLog::OS), "OperatingSystemSwiftTasks: failed to find "
170+
"task address in thread local storage");
171+
task_addrs.push_back(std::nullopt);
172+
continue;
173+
}
174+
if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS)
175+
task_addrs.push_back(std::nullopt);
176+
else
177+
task_addrs.push_back(*task_addr);
163178
}
164-
if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS)
165-
return std::nullopt;
166-
return *task_addr;
179+
return task_addrs;
167180
}
168181

169182
static std::optional<uint64_t> FindTaskId(addr_t task_addr, Process &process) {
@@ -189,10 +202,13 @@ bool OperatingSystemSwiftTasks::UpdateThreadList(ThreadList &old_thread_list,
189202
Log *log = GetLog(LLDBLog::OS);
190203
LLDB_LOG(log, "OperatingSystemSwiftTasks: Updating thread list");
191204

192-
for (const ThreadSP &real_thread : core_thread_list.Threads()) {
193-
std::optional<addr_t> task_addr =
194-
FindTaskAddress(m_task_inspector, *real_thread);
205+
ThreadCollection::ThreadIterable locked_core_threads =
206+
core_thread_list.Threads();
207+
llvm::SmallVector<std::optional<addr_t>> task_addrs =
208+
FindTaskAddresses(m_task_inspector, locked_core_threads);
195209

210+
for (const auto &[real_thread, task_addr] :
211+
llvm::zip(locked_core_threads, task_addrs)) {
196212
// If this is not a thread running a Task, add it to the list as is.
197213
if (!task_addr) {
198214
new_thread_list.AddThread(real_thread);

0 commit comments

Comments
 (0)