Skip to content

Commit 484d040

Browse files
authored
[lldb] Fix source line annotations for libsanitizers traces (#154247)
When providing allocation and deallocation traces, the ASan compiler-rt runtime already provides call addresses (`TracePCType::Calls`). On Darwin, system sanitizers (libsanitizers) provides return address. It also discards a few non-user frames at the top of the stack, because these internal libmalloc/libsanitizers stack frames do not provide any value when diagnosing memory errors. Introduce and add handling for `TracePCType::ReturnsNoZerothFrame` to cover this case and enable libsanitizers traces line-level testing. rdar://157596927 --- Commit 1 is a mechanical refactoring to introduce and adopt `TracePCType` enum to replace `pcs_are_call_addresses` bool. It preserve the current behavior: ``` pcs_are_call_addresses: false -> TracePCType::Returns (default) true -> TracePCType::Calls ``` Best reviewed commit by commit.
1 parent 7d33743 commit 484d040

File tree

13 files changed

+83
-57
lines changed

13 files changed

+83
-57
lines changed

lldb/include/lldb/lldb-private-enumerations.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,22 @@ enum class IterationAction {
248248
Stop,
249249
};
250250

251+
/// Specifies the type of PCs when creating a `HistoryThread`.
252+
/// - `Returns` - Usually, when LLDB unwinds the stack or we retrieve a stack
253+
/// trace via `backtrace()` we are collecting return addresses (except for the
254+
/// topmost frame which is the actual PC). LLDB then maps these return
255+
/// addresses back to call addresses to give accurate source line annotations.
256+
/// - `ReturnsNoZerothFrame` - Some trace providers (e.g., libsanitizers traces)
257+
/// collect return addresses but prune the topmost frames, so we should skip
258+
/// the special treatment of frame 0.
259+
/// - `Calls` - Other trace providers (e.g., ASan compiler-rt runtime) already
260+
/// perform this mapping, so we need to prevent LLDB from doing it again.
261+
enum class HistoryPCType {
262+
Returns, ///< PCs are return addresses, except for topmost frame.
263+
ReturnsNoZerothFrame, ///< All PCs are return addresses.
264+
Calls ///< PCs are call addresses.
265+
};
266+
251267
inline std::string GetStatDescription(lldb_private::StatisticKind K) {
252268
switch (K) {
253269
case StatisticKind::ExpressionSuccessful:

lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,9 @@ InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo(
266266

267267
// We gather symbolication addresses above, so no need for HistoryThread to
268268
// try to infer the call addresses.
269-
bool pcs_are_call_addresses = true;
270-
ThreadSP new_thread_sp = std::make_shared<HistoryThread>(
271-
*process_sp, tid, PCs, pcs_are_call_addresses);
269+
auto pc_type = HistoryPCType::Calls;
270+
ThreadSP new_thread_sp =
271+
std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type);
272272

273273
// Save this in the Process' ExtendedThreadList so a strong pointer retains
274274
// the object

lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@ InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo(
324324

325325
// We gather symbolication addresses above, so no need for HistoryThread to
326326
// try to infer the call addresses.
327-
bool pcs_are_call_addresses = true;
328-
ThreadSP new_thread_sp = std::make_shared<HistoryThread>(
329-
*process_sp, tid, PCs, pcs_are_call_addresses);
327+
auto pc_type = HistoryPCType::Calls;
328+
ThreadSP new_thread_sp =
329+
std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type);
330330
std::string stop_reason_description = GetStopReasonDescription(info);
331331
new_thread_sp->SetName(stop_reason_description.c_str());
332332

lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ ReportRetriever::RetrieveReportData(const ProcessSP process_sp) {
8383
options.SetAutoApplyFixIts(false);
8484
options.SetLanguage(eLanguageTypeObjC_plus_plus);
8585

86-
if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) {
86+
if (auto [m, _] = GetPreferredAsanModule(process_sp->GetTarget()); m) {
8787
SymbolContextList sc_list;
8888
sc_list.Append(SymbolContext(std::move(m)));
8989
options.SetPreferredSymbolContexts(std::move(sc_list));

lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
namespace lldb_private {
1515

16-
lldb::ModuleSP GetPreferredAsanModule(const Target &target) {
17-
// Currently only supported on Darwin.
16+
std::tuple<lldb::ModuleSP, HistoryPCType>
17+
GetPreferredAsanModule(const Target &target) {
18+
// Currently only Darwin provides ASan runtime support as part of the OS
19+
// (libsanitizers).
1820
if (!target.GetArchitecture().GetTriple().isOSDarwin())
19-
return nullptr;
21+
return {nullptr, HistoryPCType::Calls};
2022

2123
lldb::ModuleSP module;
2224
llvm::Regex pattern(R"(libclang_rt\.asan_.*_dynamic\.dylib)");
@@ -29,7 +31,16 @@ lldb::ModuleSP GetPreferredAsanModule(const Target &target) {
2931
return IterationAction::Continue;
3032
});
3133

32-
return module;
34+
// `Calls` - The ASan compiler-rt runtime already massages the return
35+
// addresses into call addresses, so we don't want LLDB's unwinder to try to
36+
// locate the previous instruction again as this might lead to us reporting
37+
// a different line.
38+
// `ReturnsNoZerothFrame` - Darwin, but not ASan compiler-rt implies
39+
// libsanitizers which collects return addresses. It also discards a few
40+
// non-user frames at the top of the stack.
41+
auto pc_type =
42+
(module ? HistoryPCType::Calls : HistoryPCType::ReturnsNoZerothFrame);
43+
return {module, pc_type};
3344
}
3445

3546
} // namespace lldb_private

lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@
99
#ifndef LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H
1010
#define LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H
1111

12+
#include <tuple>
13+
1214
#include "lldb/lldb-forward.h"
15+
#include "lldb/lldb-private-enumerations.h"
1316

1417
namespace lldb_private {
1518

16-
class Target;
17-
1819
/// On Darwin, if LLDB loaded libclang_rt, it's coming from a locally built
1920
/// compiler-rt, and we should prefer it in favour of the system sanitizers
2021
/// when running InstrumentationRuntime utility expressions that use symbols
2122
/// from the sanitizer libraries. This helper searches the target for such a
2223
/// dylib. Returns nullptr if no such dylib was found.
23-
lldb::ModuleSP GetPreferredAsanModule(const Target &target);
24+
std::tuple<lldb::ModuleSP, HistoryPCType>
25+
GetPreferredAsanModule(const Target &target);
2426

2527
} // namespace lldb_private
2628

lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,9 @@ const char *memory_history_asan_command_format =
9191
t;
9292
)";
9393

94-
static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
95-
ValueObjectSP return_value_sp,
96-
const char *type,
97-
const char *thread_name,
98-
HistoryThreads &result) {
94+
static void CreateHistoryThreadFromValueObject(
95+
ProcessSP process_sp, ValueObjectSP return_value_sp, HistoryPCType pc_type,
96+
const char *type, const char *thread_name, HistoryThreads &result) {
9997
std::string count_path = "." + std::string(type) + "_count";
10098
std::string tid_path = "." + std::string(type) + "_tid";
10199
std::string trace_path = "." + std::string(type) + "_trace";
@@ -128,12 +126,8 @@ static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
128126
pcs.push_back(pc);
129127
}
130128

131-
// The ASAN runtime already massages the return addresses into call
132-
// addresses, we don't want LLDB's unwinder to try to locate the previous
133-
// instruction again as this might lead to us reporting a different line.
134-
bool pcs_are_call_addresses = true;
135129
HistoryThread *history_thread =
136-
new HistoryThread(*process_sp, tid, pcs, pcs_are_call_addresses);
130+
new HistoryThread(*process_sp, tid, pcs, pc_type);
137131
ThreadSP new_thread_sp(history_thread);
138132
std::ostringstream thread_name_with_number;
139133
thread_name_with_number << thread_name << " Thread " << tid;
@@ -176,7 +170,8 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
176170
options.SetAutoApplyFixIts(false);
177171
options.SetLanguage(eLanguageTypeObjC_plus_plus);
178172

179-
if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) {
173+
auto [m, pc_type] = GetPreferredAsanModule(process_sp->GetTarget());
174+
if (m) {
180175
SymbolContextList sc_list;
181176
sc_list.Append(SymbolContext(std::move(m)));
182177
options.SetPreferredSymbolContexts(std::move(sc_list));
@@ -197,10 +192,10 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
197192
if (!return_value_sp)
198193
return result;
199194

200-
CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free",
201-
"Memory deallocated by", result);
202-
CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc",
203-
"Memory allocated by", result);
195+
CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,
196+
"free", "Memory deallocated by", result);
197+
CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,
198+
"alloc", "Memory allocated by", result);
204199

205200
return result;
206201
}

lldb/source/Plugins/Process/Utility/HistoryThread.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,12 @@ using namespace lldb_private;
2727

2828
HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
2929
std::vector<lldb::addr_t> pcs,
30-
bool pcs_are_call_addresses)
30+
HistoryPCType pc_type)
3131
: Thread(process, tid, true), m_framelist_mutex(), m_framelist(),
3232
m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(),
3333
m_thread_name(), m_originating_unique_thread_id(tid),
3434
m_queue_id(LLDB_INVALID_QUEUE_ID) {
35-
m_unwinder_up =
36-
std::make_unique<HistoryUnwind>(*this, pcs, pcs_are_call_addresses);
35+
m_unwinder_up = std::make_unique<HistoryUnwind>(*this, pcs, pc_type);
3736
Log *log = GetLog(LLDBLog::Object);
3837
LLDB_LOGF(log, "%p HistoryThread::HistoryThread", static_cast<void *>(this));
3938
}

lldb/source/Plugins/Process/Utility/HistoryThread.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ namespace lldb_private {
2727
/// process execution
2828
///
2929
/// This subclass of Thread is used to provide a backtrace from earlier in
30-
/// process execution. It is given a backtrace list of pc addresses and it
31-
/// will create stack frames for them.
30+
/// process execution. It is given a backtrace list of pcs (return or call
31+
/// addresses) and it will create stack frames for them.
3232

3333
class HistoryThread : public lldb_private::Thread {
3434
public:
3535
HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
3636
std::vector<lldb::addr_t> pcs,
37-
bool pcs_are_call_addresses = false);
37+
HistoryPCType pc_type = HistoryPCType::Returns);
3838

3939
~HistoryThread() override;
4040

lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ using namespace lldb_private;
2424
// Constructor
2525

2626
HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs,
27-
bool pcs_are_call_addresses)
28-
: Unwind(thread), m_pcs(pcs),
29-
m_pcs_are_call_addresses(pcs_are_call_addresses) {}
27+
HistoryPCType pc_type)
28+
: Unwind(thread), m_pcs(pcs), m_pc_type(pc_type) {}
3029

3130
// Destructor
3231

@@ -52,6 +51,17 @@ HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) {
5251
return rctx;
5352
}
5453

54+
static bool BehavesLikeZerothFrame(HistoryPCType pc_type, uint32_t frame_idx) {
55+
switch (pc_type) {
56+
case HistoryPCType::Returns:
57+
return (frame_idx == 0);
58+
case HistoryPCType::ReturnsNoZerothFrame:
59+
return false;
60+
case HistoryPCType::Calls:
61+
return true;
62+
}
63+
}
64+
5565
bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
5666
lldb::addr_t &pc,
5767
bool &behaves_like_zeroth_frame) {
@@ -61,10 +71,7 @@ bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
6171
if (frame_idx < m_pcs.size()) {
6272
cfa = frame_idx;
6373
pc = m_pcs[frame_idx];
64-
if (m_pcs_are_call_addresses)
65-
behaves_like_zeroth_frame = true;
66-
else
67-
behaves_like_zeroth_frame = (frame_idx == 0);
74+
behaves_like_zeroth_frame = BehavesLikeZerothFrame(m_pc_type, frame_idx);
6875
return true;
6976
}
7077
return false;

0 commit comments

Comments
 (0)