[lldb/DWARF] Reland: Use DW_AT_call_pc to determine artificial frame address
Reland with changes: the test modified in this change originally failed on a Debian/x86_64 builder, and I suspect the cause was that lldb looked up the line location for an artificial frame by subtracting 1 from the frame's address. For artificial frames, the subtraction must not happen because the address is already exact. --- lldb currently guesses the address to use when creating an artificial frame (i.e., a frame constructed by determining the sequence of (tail) calls which must have happened). Guessing the address creates problems -- use the actual address provided by the DW_AT_call_pc attribute instead. Depends on D76336. rdar://60307600 Differential Revision: https://reviews.llvm.org/D76337
This commit is contained in:
@@ -236,13 +236,17 @@ void StackFrameList::GetOnlyConcreteFramesUpTo(uint32_t end_idx,
|
||||
m_frames.resize(num_frames);
|
||||
}
|
||||
|
||||
/// A sequence of calls that comprise some portion of a backtrace. Each frame
|
||||
/// is represented as a pair of a callee (Function *) and an address within the
|
||||
/// callee.
|
||||
using CallSequence = std::vector<std::pair<Function *, addr_t>>;
|
||||
|
||||
/// Find the unique path through the call graph from \p begin (with return PC
|
||||
/// \p return_pc) to \p end. On success this path is stored into \p path, and
|
||||
/// on failure \p path is unchanged.
|
||||
static void FindInterveningFrames(Function &begin, Function &end,
|
||||
ExecutionContext &exe_ctx, Target &target,
|
||||
addr_t return_pc,
|
||||
std::vector<Function *> &path,
|
||||
addr_t return_pc, CallSequence &path,
|
||||
ModuleList &images, Log *log) {
|
||||
LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}",
|
||||
begin.GetDisplayName(), end.GetDisplayName(), return_pc);
|
||||
@@ -275,24 +279,27 @@ static void FindInterveningFrames(Function &begin, Function &end,
|
||||
// Fully explore the set of functions reachable from the first edge via tail
|
||||
// calls in order to detect ambiguous executions.
|
||||
struct DFS {
|
||||
std::vector<Function *> active_path = {};
|
||||
std::vector<Function *> solution_path = {};
|
||||
CallSequence active_path = {};
|
||||
CallSequence solution_path = {};
|
||||
llvm::SmallPtrSet<Function *, 2> visited_nodes = {};
|
||||
bool ambiguous = false;
|
||||
Function *end;
|
||||
ModuleList &images;
|
||||
Target ⌖
|
||||
ExecutionContext &context;
|
||||
|
||||
DFS(Function *end, ModuleList &images, ExecutionContext &context)
|
||||
: end(end), images(images), context(context) {}
|
||||
DFS(Function *end, ModuleList &images, Target &target,
|
||||
ExecutionContext &context)
|
||||
: end(end), images(images), target(target), context(context) {}
|
||||
|
||||
void search(Function &first_callee, std::vector<Function *> &path) {
|
||||
dfs(first_callee);
|
||||
void search(CallEdge &first_edge, Function &first_callee,
|
||||
CallSequence &path) {
|
||||
dfs(first_edge, first_callee);
|
||||
if (!ambiguous)
|
||||
path = std::move(solution_path);
|
||||
}
|
||||
|
||||
void dfs(Function &callee) {
|
||||
void dfs(CallEdge ¤t_edge, Function &callee) {
|
||||
// Found a path to the target function.
|
||||
if (&callee == end) {
|
||||
if (solution_path.empty())
|
||||
@@ -312,13 +319,16 @@ static void FindInterveningFrames(Function &begin, Function &end,
|
||||
}
|
||||
|
||||
// Search the calls made from this callee.
|
||||
active_path.push_back(&callee);
|
||||
active_path.emplace_back(&callee, LLDB_INVALID_ADDRESS);
|
||||
for (const auto &edge : callee.GetTailCallingEdges()) {
|
||||
Function *next_callee = edge->GetCallee(images, context);
|
||||
if (!next_callee)
|
||||
continue;
|
||||
|
||||
dfs(*next_callee);
|
||||
addr_t tail_call_pc = edge->GetCallInstPC(callee, target);
|
||||
active_path.back().second = tail_call_pc;
|
||||
|
||||
dfs(*edge, *next_callee);
|
||||
if (ambiguous)
|
||||
return;
|
||||
}
|
||||
@@ -326,7 +336,7 @@ static void FindInterveningFrames(Function &begin, Function &end,
|
||||
}
|
||||
};
|
||||
|
||||
DFS(&end, images, exe_ctx).search(*first_callee, path);
|
||||
DFS(&end, images, target, exe_ctx).search(*first_edge, *first_callee, path);
|
||||
}
|
||||
|
||||
/// Given that \p next_frame will be appended to the frame list, synthesize
|
||||
@@ -379,7 +389,7 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
|
||||
|
||||
// Try to find the unique sequence of (tail) calls which led from next_frame
|
||||
// to prev_frame.
|
||||
std::vector<Function *> path;
|
||||
CallSequence path;
|
||||
addr_t return_pc = next_reg_ctx_sp->GetPC();
|
||||
Target &target = *target_sp.get();
|
||||
ModuleList &images = next_frame.CalculateTarget()->GetImages();
|
||||
@@ -389,14 +399,17 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
|
||||
path, images, log);
|
||||
|
||||
// Push synthetic tail call frames.
|
||||
for (Function *callee : llvm::reverse(path)) {
|
||||
for (auto calleeInfo : llvm::reverse(path)) {
|
||||
Function *callee = calleeInfo.first;
|
||||
uint32_t frame_idx = m_frames.size();
|
||||
uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex();
|
||||
addr_t cfa = LLDB_INVALID_ADDRESS;
|
||||
bool cfa_is_valid = false;
|
||||
addr_t pc =
|
||||
callee->GetAddressRange().GetBaseAddress().GetLoadAddress(&target);
|
||||
constexpr bool behaves_like_zeroth_frame = false;
|
||||
addr_t pc = calleeInfo.second;
|
||||
// We do not want to subtract 1 from this PC, as it's the actual address
|
||||
// of the tail-calling branch instruction. This address is provided by the
|
||||
// compiler via DW_AT_call_pc.
|
||||
constexpr bool behaves_like_zeroth_frame = true;
|
||||
SymbolContext sc;
|
||||
callee->CalculateSymbolContext(&sc);
|
||||
auto synth_frame = std::make_shared<StackFrame>(
|
||||
@@ -404,7 +417,7 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
|
||||
cfa_is_valid, pc, StackFrame::Kind::Artificial,
|
||||
behaves_like_zeroth_frame, &sc);
|
||||
m_frames.push_back(synth_frame);
|
||||
LLDB_LOG(log, "Pushed frame {0}", callee->GetDisplayName());
|
||||
LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc);
|
||||
}
|
||||
|
||||
// If any frames were created, adjust next_frame's index.
|
||||
|
||||
Reference in New Issue
Block a user