Skip to content

Commit 7458f53

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: methods are rewritten work with vectors of threads and addresses, instead of individual values. [1]: https://discourse.llvm.org/t/rfc-a-new-vectorized-memory-read-packet/
1 parent 088ed7e commit 7458f53

File tree

3 files changed

+160
-60
lines changed

3 files changed

+160
-60
lines changed

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

Lines changed: 117 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2915,19 +2915,6 @@ std::optional<lldb::addr_t> SwiftLanguageRuntime::TrySkipVirtualParentProlog(
29152915
return pc_value;
29162916
}
29172917

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-
29312918
/// Compute the location where the Task pointer for `real_thread` is stored by
29322919
/// the runtime.
29332920
static llvm::Expected<lldb::addr_t>
@@ -2955,48 +2942,133 @@ ComputeTaskAddrLocationFromThreadLocalStorage(Thread &real_thread) {
29552942
#endif
29562943
}
29572944

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

2964-
if (auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
2965-
it != m_tid_to_task_addr_location.end()) {
3003+
for (auto [idx, thread] : llvm::enumerate(threads)) {
3004+
Thread &real_thread =
3005+
thread->GetBackingThread() ? *thread->GetBackingThread() : *thread;
3006+
3007+
auto it = m_tid_to_task_addr_location.find(real_thread.GetID());
3008+
if (it != m_tid_to_task_addr_location.end()) {
3009+
addr_locations.push_back(it->second);
29663010
#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);
3011+
// In assert builds, check that caching did not produce incorrect results.
3012+
llvm::Expected<lldb::addr_t> task_addr_location =
3013+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
3014+
assert(task_addr_location);
3015+
assert(it->second == *task_addr_location);
29723016
#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(),
3017+
continue;
3018+
}
3019+
addr_locations.push_back(
3020+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread));
3021+
}
3022+
return addr_locations;
3023+
}
3024+
3025+
llvm::SmallVector<llvm::Expected<addr_t>>
3026+
TaskInspector::GetTaskAddrFromThreadLocalStorage(
3027+
llvm::ArrayRef<Thread *> threads) {
3028+
if (threads.empty())
3029+
return {};
3030+
3031+
llvm::SmallVector<llvm::Expected<addr_t>> addr_locations =
3032+
GetTaskAddrLocations(threads);
3033+
3034+
Process &process = *threads[0]->GetProcess();
3035+
llvm::SmallVector<llvm::Expected<addr_t>> mem_read_results =
3036+
MultiReadPointers(process, addr_locations);
3037+
3038+
for (auto [idx, thread] : llvm::enumerate(threads)) {
3039+
Thread &real_thread =
3040+
thread->GetBackingThread() ? *thread->GetBackingThread() : *thread;
3041+
user_id_t tid = real_thread.GetID();
3042+
3043+
// If the read was successful, cache the address.
3044+
if (mem_read_results[idx]) {
3045+
m_tid_to_task_addr_location[tid] = *addr_locations[idx];
3046+
continue;
3047+
}
3048+
3049+
// For unsuccessful reads whose address was not cached, don't try again.
3050+
if (!m_tid_to_task_addr_location.erase(tid))
3051+
continue;
3052+
3053+
LLDB_LOG_ERROR(GetLog(LLDBLog::OS), mem_read_results[idx].takeError(),
29803054
"TaskInspector: evicted task location address due to "
29813055
"invalid memory read: {0}");
2982-
}
29833056

2984-
llvm::Expected<lldb::addr_t> task_addr_location =
2985-
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
2986-
if (!task_addr_location)
2987-
return task_addr_location;
3057+
// The cached address could not be loaded. "This should never happen", but
3058+
// recompute the address and try again for completeness.
3059+
llvm::Expected<addr_t> task_addr_loc =
3060+
ComputeTaskAddrLocationFromThreadLocalStorage(real_thread);
3061+
if (!task_addr_loc) {
3062+
mem_read_results[idx] = std::move(task_addr_loc);
3063+
continue;
3064+
}
29883065

2989-
llvm::Expected<lldb::addr_t> task_addr =
2990-
ReadTaskAddr(*task_addr_location, *thread.GetProcess());
3066+
mem_read_results[idx] = ReadPointer(process, *task_addr_loc);
3067+
if (mem_read_results[idx])
3068+
m_tid_to_task_addr_location[tid] = *task_addr_loc;
3069+
}
29913070

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;
3071+
return mem_read_results;
30003072
}
30013073

30023074
namespace {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,17 @@ class TaskInspector {
921921
llvm::Expected<lldb::addr_t>
922922
GetTaskAddrFromThreadLocalStorage(Thread &thread);
923923

924+
/// Inspects thread local storage to find the address of the currently
925+
/// executing task, if any.
926+
llvm::SmallVector<llvm::Expected<lldb::addr_t>>
927+
GetTaskAddrFromThreadLocalStorage(llvm::ArrayRef<Thread *> threads);
928+
924929
private:
930+
/// For each thread in `threads`, return the location of the its task
931+
/// pointer, if it exists.
932+
llvm::SmallVector<llvm::Expected<lldb::addr_t>>
933+
GetTaskAddrLocations(llvm::ArrayRef<Thread *> threads);
934+
925935
llvm::DenseMap<uint64_t, lldb::addr_t> m_tid_to_task_addr_location;
926936
};
927937

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

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,34 @@ 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 (llvm::Expected<addr_t> &task_addr :
167+
task_inspector.GetTaskAddrFromThreadLocalStorage(threads)) {
168+
if (!task_addr) {
169+
LLDB_LOG_ERROR(
170+
GetLog(LLDBLog::OS), task_addr.takeError(),
171+
"OperatingSystemSwiftTasks: failed to find task address in "
172+
"thread local storage: {0}");
173+
task_addrs.push_back(std::nullopt);
174+
continue;
175+
}
176+
if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS)
177+
task_addrs.push_back(std::nullopt);
178+
else
179+
task_addrs.push_back(*task_addr);
163180
}
164-
if (*task_addr == 0 || *task_addr == LLDB_INVALID_ADDRESS)
165-
return std::nullopt;
166-
return *task_addr;
181+
return task_addrs;
167182
}
168183

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

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

212+
for (const auto &[real_thread, task_addr] :
213+
llvm::zip(locked_core_threads, task_addrs)) {
196214
// If this is not a thread running a Task, add it to the list as is.
197215
if (!task_addr) {
198216
new_thread_list.AddThread(real_thread);

0 commit comments

Comments
 (0)